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
함께 좋은 많은 문제를 디버깅하기에 충분한다.
sh
스크립트configure
수많은 UN * X 패키지의 빌드 프로세스의 일부로 사용되는 '비교적 간단한'이 아니다.