매우 빠른 연구를 한 후에 Bash 는 Turing-complete language 인 것 같습니다 .
왜 Bash가 비교적 간단한 스크립트를 작성하기 위해 거의 독점적으로 사용됩니까? Bash 셸은 Linux와 함께 제공되므로 널리 사용되는 다른 컴퓨터 언어에 필요한 외부 인터프리터 나 컴파일러없이 셸 스크립트를 실행할 수 있습니다. 이것은 어떤 경우에는 언어 자체의 평범함을 보완 할 수있는 큰 이점입니다.
그렇다면 그러한 프로그램이 얼마나 복잡 할 수 있는가에 한계가 있습니까? 복잡한 프로그램을 작성하는 데 순수 Bash가 사용됩니까? 순수한 Bash로 파일 컴프레서 / 디 컴프레서를 쓸 수 있습니까? 컴파일러? 간단한 비디오 게임?
디버깅 도구가 매우 제한되어 있기 때문에 매우 드물게 사용됩니까?
답변
Bash는 Turing-complete 언어 인 것 같습니다
튜링 완전성 의 개념은 유용성, 표현성, 이해력, 속도 등 프로그래밍 언어에 유용한 다른 많은 개념과 완전히 분리되어 있습니다 .
만약 Turing-completeness가 우리에게 필요한 전부라면, 우리는 어셈블리 언어가 아닌 프로그래밍 언어 가 전혀 없을 것 입니다 . CPU도 Turing-complete이기 때문에 컴퓨터 프로그래머는 모두 머신 코드로 작성 합니다.
Bash가 비교적 간단한 스크립트를 작성하기 위해 거의 독점적으로 사용되는 이유는 무엇입니까?
configure
GNU Autoconf 의 스크립트 출력 과 같은 크고 복잡한 쉘 스크립트 는 여러 가지 이유로 비정형 적입니다.
-
비교적 최근까지 POSIX 호환 쉘을 어디에나 사용할 수 없었습니다 .
많은 시스템, 특히 구형 시스템에는 기술적으로 시스템 어딘가에 POSIX 호환 쉘이 있지만 다음과 같은 예측 가능한 위치에는 없을 수 있습니다
/bin/sh
. 쉘 스크립트를 작성 중이고 다른 시스템에서 실행해야한다면 어떻게 shebang 라인 을 작성 합니까? 한 가지 옵션은을 사용하는/bin/sh
것이지만 그러한 시스템에서 실행되는 경우 POSIX Bourne 쉘 방언으로 제한합니다.POSIX 이전 Bourne 쉘에는 내장 연산 기능도 없습니다. 전화를 걸
expr
거나bc
완료해야합니다.POSIX 셸을 사용하더라도 Perl이 1990 년대 초 처음으로 인기를 얻은 이후 유닉스 스크립팅 언어에서 발견 할 수있는 연관 배열 및 기타 기능 에 대해 빠졌습니다 .
역사의 사실은 현대 Bourne 패밀리 쉘 스크립트 인터프리터의 강력한 기능 중 많은 것을 무시하는 전통이 수십 년 동안 존재한다는 것을 의미합니다.
사실 현재까지도 계속되고 있습니다. Bash는 버전 4까지 연관 배열을 얻지 못했지만 Bash 3을 기반으로 아직도 사용중인 시스템 수에 놀라실 것입니다. 라이센싱 이유 -Unix / Linux 서버는 종종 오랜 시간 동안 프로덕션 환경에서 거의 그대로 유지되므로 CentOS 5 박스와 같은 Bash 3을 실행하는 안정적인 이전 시스템이있을 수 있습니다. 환경에 이러한 시스템이 있으면 해당 시스템에서 실행되어야하는 셸 스크립트에서 연관 배열을 사용할 수 없습니다.
그 문제에 대한 당신의 대답은 “현대”시스템 만 쓰기 쉘 스크립트, 당신은 다음 대부분의 유닉스 쉘의 마지막 공통 기준 포인트는 사실에 대처해야한다는 것입니다 경우 POSIX 쉘의 표준 이 된 이후 크게 변경되지 않습니다, 이 표준을 기반으로 한 다양한 쉘이 있지만 모두 표준과 다른 수준으로 분기되었습니다. 다시 연관 배열을 위해
bash
,zsh
그리고ksh93
모든 기능을 가지고 있지만, 여러 구현 호환성이있다. 당신의 선택은, 다음,하는 것입니다 만 배쉬를 사용하거나 전용 zsh을 사용하거나 에만 사용ksh93
.이 문제에 대한 대답이 “그래서 Bash 4를 설치하십시오”또는
ksh93
“어떻게합니까?” 많은 경우에 허용되지 않습니다. 기본값이 중요합니다. -
Bourne 제품군 쉘 스크립트 언어는 모듈을 지원하지 않습니다 .
쉘 스크립트에서 가장 가까운 모듈 시스템에 접근 할 수있는
.
명령source
은보다 현대적인 Bourne 쉘 변형 이라고 하는 명령 입니다.이 명령 은 적절한 모듈 시스템에 비해 여러 수준에서 실패하며, 가장 기본적인 것은 이름 간격 입니다.프로그래밍 언어에 관계없이 더 큰 전체 프로그램의 단일 파일이 수천 줄을 초과하면 사람이 이해하기 시작합니다. 우리가 큰 프로그램을 많은 파일로 구성하는 이유는 그 내용을 최대 한두 문장으로 추상화 할 수 있기 때문입니다. 파일 A는 명령 행 구문 분석기, 파일 B는 네트워크 I / O 펌프, 파일 C는 라이브러리 Z와 나머지 프로그램 사이의 심입니다. 많은 파일을 단일 프로그램으로 어셈블하는 유일한 방법은 텍스트 포함입니다. 귀하의 프로그램이 합리적으로 성장할 수있는 규모에 제한을 두었습니다.
비교를 위해 C 프로그래밍 언어에 링커가없고
#include
명령문 만있는 경우와 같습니다 . 이러한 C-lite 방언에는extern
또는 과 같은 키워드가 필요하지 않습니다static
. 이러한 기능은 모듈성을 허용하기 위해 존재합니다. -
POSIX 는 변수를 단일 쉘 스크립트 함수로 범위를 지정 하는 방법 을 정의하지 않으며 파일보다 훨씬 적습니다.
이것은 모든 변수를 효과적으로 전역 적으로 만들어 모듈 성과 구성 성을 손상시킵니다.
확실히에 -이 솔루션은 이후 POSIX 쉘이에있는
bash
,ksh93
그리고zsh
적어도 -하지만 그건 그냥 위의 점 1을 제공합니다.GNU Autoconf 매크로 작성에 대한 스타일 가이드에서이 효과를 확인할 수 있습니다. 여기에서 변수 이름 앞에 매크로 자체의 이름을 붙일 것을 권장 합니다. 제로.
C조차도이 점수에서 1 마일 더 좋습니다. 대부분의 C 프로그램은 주로 함수 로컬 변수로 작성 될뿐만 아니라 블록 범위 지정을 지원하므로 단일 함수 내의 여러 블록이 교차 오염없이 변수 이름을 재사용 할 수 있습니다.
-
쉘 프로그래밍 언어에는 표준 라이브러리가 없습니다.
쉘 스크립팅 언어의 표준 라이브러리는의 내용
PATH
이라고 주장 할 수는 있지만 결과를 얻으려면 쉘 스크립트가 다른 전체 프로그램, 아마도 더 강력한 언어로 작성된 다른 프로그램을 호출해야한다고 말합니다. 로 시작하십시오.Perl의 CPAN 과 같이 널리 사용되는 쉘 유틸리티 라이브러리의 아카이브도 없습니다 . 사용 가능한 많은 타사 유틸리티 코드 라이브러리가 없으면 프로그래머는 더 많은 코드를 직접 작성해야하므로 생산성이 떨어집니다.
대부분의 쉘 스크립트가 유용한 작업을 수행하기 위해 일반적으로 C로 작성된 외부 프로그램에 의존한다는 사실을 무시하더라도
pipe()
→fork()
→exec()
호출 체인 의 오버 헤드가 있습니다. 이 패턴은 다른 OS에서 IPC 및 프로세스 실행에 비해 Unix에서 상당히 효율적 이지만 여기서는 훨씬 더 효율적인 다른 스크립팅 언어 의 서브 루틴 호출 로 수행하는 작업을 효과적으로 대체합니다 . 이것은 쉘 스크립트 실행 속도의 상한을 심각하게 제한합니다. -
셸 스크립트에는 병렬 실행을 통해 성능을 향상시킬 수있는 기본 제공 기능이 거의 없습니다.
Bourne의 포탄 한
&
,wait
이에 대한 파이프 라인,하지만하지 CPU 또는 I / O 병렬 처리를 달성하기위한 여러 프로그램을 구성하기위한 대부분에만 유용합니다. 쉘 스크립팅만으로 코어 를 페깅 하거나 RAID 어레이를 포화 시킬 수 없을 가능성이 높으며 , 그렇게하면 다른 언어에서 훨씬 더 높은 성능을 달성 할 수 있습니다.특히 파이프 라인은 병렬 실행을 통해 성능을 향상시키는 약한 방법입니다. 두 개의 프로그램 만 병렬로 실행할 수 있으며, 두 개 중 하나는 특정 시점에 다른 I / O 와의 I / O에서 차단 될 수 있습니다 .
이 같은이 주위 후기의 방법입니다
xargs -P
및 GNU는parallel
하지만, 이것은 단지 위의 4를 가리 키도록 양도한다.다중 프로세서 시스템을 최대한 활용할 수있는 기본 제공 기능이 없기 때문에 쉘 스크립트는 시스템의 모든 프로세서를 사용할 수있는 언어로 작성된 프로그램보다 항상 느리게 진행됩니다. GNU Autoconf
configure
스크립트 예제를 다시 살펴보면 시스템의 코어 수를 두 배로 늘리면 실행 속도를 향상시키는 데 거의 도움이되지 않습니다. -
이렇게하면 다른 프로그래밍 언어로 쉽게 수행 할 수있는 작업을 수행 할 수 없습니다.
우선, 프로그램 메모리에서 다른 데이터 구조를 간접적으로 참조 할 수 없다는 것은 내장 된 데이터 구조로 제한된다는 의미 입니다. 쉘에는 연관 배열 이있을 수 있지만 어떻게 구현됩니까? 이 몇 가지 가능성, 다른 장단점에 각각 : 레드 – 블랙 트리 , AVL 나무 와 해시 테이블은 가장 일반적이지만, 다른 사람이있다. 다른 트레이드 오프 집합이 필요한 경우 참조가 없으면 여러 유형의 고급 데이터 구조를 수동으로 롤링 할 방법이 없기 때문에 문제가 발생합니다. 당신은 당신이 주어진 것에 고착되어 있습니다.
또는 종속 그래프 를 모델링하는 데 필요할 수있는 방향성 비순환 그래프 와 같이 쉘 스크립트 인터프리터에 적절한 대안이 내장되지 않은 데이터 구조가 필요한 경우도 있습니다 . 나는 수십 년 동안 프로그래밍을 해왔고 쉘 스크립트에서 그렇게 할 수있는 유일한 방법 은 심볼 링크를 가짜 참조로 사용하여 파일 시스템 을 악용하는 것 입니다. 이는 Turing-completeness에만 의존 할 때 얻을 수있는 일종의 솔루션으로, 솔루션이 우아하고 빠르거나 이해하기 쉬운 지 여부에 대해서는 아무 것도 알려주지 않습니다.
고급 데이터 구조는 포인터와 참조에 한 가지 용도 일뿐입니다. Bourne 패밀리 쉘 스크립팅 언어로는 쉽게 수행 할 수없는 다른 애플리케이션 이 많이 있습니다.
나는 계속해서 갈 수 있지만, 당신이 여기서 요점을 얻고 있다고 생각합니다. 간단히 말해서, 유닉스 타입 시스템을위한 더 강력한 프로그래밍 언어 가 많이 있습니다.
이것은 어떤 경우에는 언어 자체의 평범함을 보완 할 수있는 큰 이점입니다.
이것이 바로 GNU Autoconf가 configure
스크립트 출력을 위해 Bourne 쉘 스크립트 언어의 의도적으로 제한된 하위 집합을 사용하는 이유입니다 . 따라서 configure
스크립트는 거의 모든 곳에서 실행됩니다.
GNU Autoconf 개발자보다 이식성이 뛰어난 Bourne 쉘 방언을 작성하는 데있어서 더 큰 신자들 그룹을 찾지 못할 것입니다. 그러나 그들 자신의 창작물은 주로 Perl과 약간 m4
의 쉘로 작성되었습니다. 스크립트; Autoconf의 출력 만이 순수한 Bourne 쉘 스크립트입니다. 그것이 “Bourne everywhere”개념이 얼마나 유용한 지에 대한 의문을 제기하지 않는다면, 나는 무엇을할지 모른다.
그렇다면 그러한 프로그램이 얼마나 복잡 할 수 있는가에 한계가 있습니까?
Turing-completeness 관찰에서 알 수 있듯이 기술적으로 말하면 아닙니다.
그러나 이는 임의의 큰 쉘 스크립트가 작성하기 쉽고, 디버깅하기 쉽고, 실행하기에 빠르다는 말과 다릅니다.
순수한 bash로 파일 컴프레서 / 디 컴프레서를 쓸 수 있습니까?
“순수한”배쉬 PATH
? 압축기는 아마도 echo
16 진수 이스케이프 시퀀스를 사용하여 수행 할 수 있지만 상당히 고통 스럽습니다. 쉘에서 이진 데이터를 처리 할 수 없기 때문에 압축 해제 기는 이러한 방식으로 작성하는 것이 불가능할 수 있습니다 . od
셸의 기본 데이터 처리 방식 인 이진 데이터를 텍스트 형식으로 변환 하기 위해 전화를 걸었습니다 .
쉘 스크립팅을 사용하여 의도 한 방식으로 이야기를 시작하면에서 다른 프로그램을 구동하기위한 접착제로 PATH
문이 열립니다. 이제 다른 프로그래밍 언어로 수행 할 수있는 것으로 제한됩니다. 전혀 제한이 없습니다. 다른 프로그램을 호출하여 모든 능력을 얻는 쉘 스크립트 PATH
는 더 강력한 언어로 작성된 모 놀리 식 프로그램만큼 빠르지 않지만 실행됩니다.
그리고 그게 요점입니다. 빠른 실행을 위해 프로그램이 필요하거나 다른 사람의 힘을 빌리는 대신 자체적으로 강력해야하는 경우에는 쉘로 작성하지 마십시오.
간단한 비디오 게임?
여기 쉘에서 테트리스 . 당신이 찾고 있다면 다른 그러한 게임도 가능합니다.
매우 제한된 디버깅 도구 만 있습니다
디버깅 도구 지원은 프로그래밍을 지원하는 데 필요한 기능 목록에서 약 20 위를 차지했습니다. 많은 프로그래머 들이 언어에 관계없이 적절한 디버거보다 printf()
디버깅 에 훨씬 더 의존 합니다.
쉘에서, 당신은 echo
과 set -x
함께 좋은 많은 문제를 디버깅하기에 충분한다.
답변
우리는 어디에서나 걷거나 수영 할 수 있는데, 왜 자전거, 자동차, 기차, 보트, 비행기 및 기타 차량으로 귀찮게합니까? 물론, 걷기 또는 수영은 지칠 수 있지만 추가 장비가 필요하지 않은 경우 큰 이점이 있습니다.
우선 배쉬가 Turing-complete이지만 정수 (너무 크지 않음), 문자열, (1 차원) 문자열 배열 및 문자열에서 문자열로의 유한 맵 이외의 데이터를 조작하는 것은 좋지 않습니다. 다른 종류의 데이터에는 귀찮은 인코딩이 필요하므로 프로그램을 작성하기가 어렵고 실제로는 성능이 좋지 않은 성능을 강요하기도합니다. 예를 들어, bash의 부동 소수점 연산은 어렵고 느립니다.
또한 bash는 환경과 상호 작용하는 방법이 거의 없습니다. 프로세스를 실행할 수 있고 (리디렉션을 통해) 간단한 종류의 파일 액세스를 수행 할 수 있습니다. Bash에는 클라이언트 측 네트워킹 클라이언트도 있습니다. Bash는 null 바이트를 충분히 쉽게 방출 할 수 printf \\0
있지만 입력에서 null 바이트를 구문 분석 할 수 없으므로 이진 데이터를 읽는 데 적합하지 않습니다. Bash는 다른 일을 직접 할 수 없습니다. 외부 프로그램을 호출해야합니다. 그리고 그것은 괜찮습니다 : 쉘은 외부 프로그램을 실행하는 주요 목적을 위해 설계되었습니다! 쉘은 프로그램을 결합하기위한 풀 언어입니다. 그러나 외부 프로그램을 실행하는 경우 프로그램을 사용할 수 있어야하며 이식성 이점이 줄어 듭니다.).
Bash에는를 제외한 강력한 프로그램을보다 쉽게 작성할 수있는 기능이 없습니다 set -e
. (유용한) 유형, 네임 스페이스, 모듈 또는 중첩 된 데이터 구조가 없습니다. 버그는 프로그래밍에서 가장 어려운 문제입니다. 버그가없는 프로그램을 작성하는 것이 언어를 선택하는 데 항상 결정적인 요소는 아니지만 bash는 그 수에 비해 순위가 낮습니다. Bash는 또한 프로그램을 결합하는 것 이외의 작업을 수행 할 때 성능면에서 좋지 않습니다.
오랫동안 bash는 Windows에서 실행되지 않았으며 오늘날에도 기본 Windows 설치에는 없으며 심지어 WSL에서도 인터페이스가 없다는 완전히 원시적으로 실행되지 않습니다. Windows의 기본 기능. Bash는 iOS에서 실행되지 않으며 기본적으로 Android에 설치되지 않습니다. 따라서 유닉스 전용 응용 프로그램을 작성하지 않는 한 bash는 전혀 이식성이 없습니다.
컴파일러를 요구하는 것은 이식성의 문제가 아닙니다. 컴파일러는 개발자 컴퓨터에서 실행됩니다. 인터프리터 또는 타사 라이브러리가 필요한 것은 문제가 될 수 있지만 Linux에서는 배포 패키지를 통해 해결 된 문제이며 Windows, Android 및 iOS에서는 사람들이 일반적으로 타사 패키지를 응용 프로그램 패키지에 번들로 제공합니다. 따라서 여러분이 염두에 두어야 할 이식성 문제는 실제 응용 프로그램에 대한 실질적인 문제가 아닙니다.
내 대답은 bash 이외의 껍질에 적용됩니다. 몇 가지 세부 사항은 셸마다 다르지만 일반적인 아이디어는 동일합니다.
답변
큰 프로그램에서 쉘 스크립트를 사용하지 않는 몇 가지 이유는 내 머리 꼭대기에 있습니다.
- 대부분의 기능은 외부 명령을 해제하여 수행되며 속도가 느립니다. 대조적으로, Perl과 같은 프로그래밍 언어는 동등
mkdir
하거나grep
내부적으로 수행 할 수 있습니다 . - C 라이브러리에 액세스하거나 직접 시스템 호출을하는 쉬운 방법은 없습니다. 예를 들어 비디오 게임을 만들기가 어렵습니다.
- 적절한 프로그래밍 언어는 복잡한 데이터 구조를 더 잘 지원합니다. Bash에는 배열과 연관 배열이 있지만 연결된 목록이나 트리를 생각하고 싶지 않습니다.
- 쉘은 텍스트 인 경우 명령을 처리하기 위해 만들어집니다. 이진 데이터 (즉, NUL 바이트 (값이 0 인 바이트)를 포함하는 변수)는 처리하기가 어렵습니다. 쉘에 약간 의존하며
zsh
약간의 지원이 있습니다. 외부 프로그램의 인터페이스는 대부분 텍스트 기반이며\0
구분자로 사용되기 때문입니다. - 또한 외부 명령으로 인해 코드와 데이터를 구분하기가 약간 어렵습니다. (즉, 때 실행하는 다른 셀에 데이터를 인용 할 때 증인 모든 문제가있다
bash -c ...
거나ssh -c ...
)