순수 Bash로 작성된 프로그램은 얼마나 복잡할까요? [폐쇄]

순수 Bash로 작성된 프로그램은 얼마나 복잡할까요? [폐쇄]

빠른 조사 끝에 Bash는튜링완전 언어이다.

왜 Bash가 상대적으로 간단한 스크립트를 작성하는 데 거의 독점적으로 사용되는지 궁금합니다. Linux에는 Bash 쉘이 함께 제공되므로 다른 널리 사용되는 컴퓨터 언어에 필요한 것처럼 외부 해석기나 컴파일러 없이 쉘 스크립트를 실행할 수 있습니다. 이는 어떤 경우에는 언어 자체의 평범함을 보완할 수 있는 큰 이점입니다.

그렇다면 그러한 프로그램이 얼마나 복잡할 수 있는지에 대한 제한이 있습니까? 복잡한 프로그램을 작성하는 데 순수 Bash를 사용할 수 있나요? 순수 Bash에서 파일 압축기/압축 해제기를 작성할 수 있습니까? 번역가? 간단한 비디오 게임?

디버깅 도구가 너무 제한적이어서 거의 사용되지 않기 때문일까요?

답변1

Bash는 Turing 완전 언어인 것 같습니다.

개념튜링 완전성언어에 유용한 다른 많은 개념과 완전히 분리됨대규모 프로그래밍: 사용성, 표현력, 이해성, 속도 등

튜링 완전성만 원한다면 프로그래밍 언어가 없을 것입니다.별말씀을요, 심지어어셈블리어. 컴퓨터 프로그래머가 직접 작성합니다.기계어 코드, 우리 CPU도 Turing Complete이기 때문입니다.

상대적으로 간단한 스크립트를 작성하는 데 Bash가 거의 독점적으로 사용되는 이유는 무엇입니까?

configureGNU Autoconf의 출력 과 같은 크고 복잡한 쉘 스크립트는 다음과 같은 여러 가지 이유로 일반적이지 않습니다.

  1. 최근까지,어디에서나 POSIX 호환 쉘을 기대할 수는 없습니다..

    많은 시스템, 특히 오래된 시스템에는 기술적으로 POSIX 호환 쉘이 있습니다.어딘가에예를 들어, /bin/sh쉘 스크립트를 작성 중이고 여러 다른 시스템에서 실행되어야 한다면 어떻게 작성하시겠습니까?셰르본 라인? 한 가지 옵션은 계속 사용하는 것이지만 /bin/sh, 그러한 시스템에서 실행되는 경우 POSIX 이전 Bourne 쉘 방언으로 제한하도록 선택하십시오.

    POSIX 이전 Bourne 셸에는 호출해야 하는 산술 기능도 내장되어 있지 않았습니다.expr또는bc이 작업을 완료하세요.

    POSIX 쉘을 사용하더라도 놓칠 수 있습니다연관 배열Perl이 처음 인기를 얻은 이후 Unix 스크립팅 언어에서 우리가 기대하게 된 기타 기능1990년대 초.

    이 역사적 사실은 현대 Bourne 쉘 스크립트 인터프리터 제품군의 많은 강력한 기능을 순전히 어디에서나 사용할 수 있다고 기대할 수 없기 때문에 수십 년 동안 무시해 온 전통이 있음을 의미합니다.

    실제로 이러한 상황은 오늘날까지 지속됩니다. Bash는 연관 배열을 얻지 못합니다.버전 4까지하지만 Bash 3을 기반으로 아직도 사용 중인 시스템이 얼마나 많은지 알면 놀랄 수도 있습니다. Apple은 2017년에도 여전히 macOS와 함께 Bash 3를 제공합니다 —분명히 라이센스상의 이유로— 그리고 Unix/Linux 서버는 영향이 거의 또는 전혀 없이 오랜 기간 동안 프로덕션에서 실행되는 경우가 많으므로 CentOS 5 시스템과 같이 Bash 3을 계속 실행하는 안정적이고 오래된 시스템이 있을 수 있습니다. 사용자 환경에 이러한 시스템이 있는 경우 해당 시스템에서 실행되어야 하는 셸 스크립트에서 연관 배열을 사용할 수 없습니다.

    이 질문에 대한 답이 "최신" 시스템용 쉘 스크립트만 작성하는 것이라면 대부분의 Unix 쉘에 대한 마지막 공통 참조 사항은 다음과 같다는 사실에 직면해야 합니다.POSIX 쉘 표준1989년 도입 이후 본질적으로 변하지 않았습니다. 이 표준을 기반으로 하는 다양한 쉘이 있지만 모두 다양한 정도에서 벗어납니다. 다시 연관 배열을 사용하면 , bashzsh모두 ksh93이 기능을 가지지만 여러 가지 구현 비호환성이 있습니다. 그렇다면 당신의 선택은오직Bash를 사용하거나오직Zsh를 사용하거나오직사용 ksh93.

    해당 질문에 대한 대답이 "그냥 Bash 4를 설치하세요" 등이라면 ksh93Perl, Python 또는 Ruby를 "그냥" 설치하면 어떨까요? 이는 대부분의 경우 허용되지 않습니다. 기본값이 중요합니다.

  2. Bourne 시리즈의 쉘 스크립트 언어는 지원되지 않습니다.기준 치수.

    셸 스크립트에서 모듈 시스템에 가장 가까운 것은 명령 .(보다 현대적인 Bourne 셸 변형이라고도 함 source)입니다. 이 명령은 적절한 모듈 시스템과 관련하여 여러 수준에서 실패합니다. 그 중 가장 기본적인 것은 다음과 같습니다.네임스페이스.

    어떤 프로그래밍 언어를 사용하든 더 큰 전체 프로그램 내의 단일 파일이 수천 줄을 초과하면 인간의 이해에 문제가 발생하기 시작합니다. 우리가 큰 프로그램을 많은 파일로 구성하는 진짜 이유는 그 내용을 최대 한두 문장으로 추상화할 수 있기 때문입니다. 파일 A는 명령줄 파서이고, 파일 B는 네트워크 I/O 펌프이고, 파일 C는 라이브러리 Z와 나머지 프로그램 사이의 심입니다. 여러 파일을 단일 프로그램으로 조합하는 유일한 방법이 텍스트 포함인 경우 프로그램이 합리적으로 커질 수 있는 크기에 대한 제한을 설정합니다.

    비교를 위해 다음과 같이C 프로그래밍 언어링커는 없고 #include명령문만 있습니다. 이러한 C-lite 방언에는 extern또는 같은 키워드가 필요하지 않습니다 static. 이러한 기능은 모듈성을 위해 존재합니다.

  3. POSIX메소드가 정의되지 않았습니다.파일은 물론 단일 쉘 스크립트 함수까지 변수 범위를 지정합니다.

    이는 효과적으로모든 변수는 전역이는 다시 모듈성과 구성성을 손상시킵니다.

    bash물론 POSIX 이후 셸에는 이 문제에 대한 해결책이 있지만, ksh93이는 zsh위의 1번 항목으로 돌아가게 됩니다.

    GNU Autoconf 매크로 작성 스타일 가이드에서 이 내용을 확인할 수 있습니다.그들은 추천한다매크로 자체의 이름을 변수 이름 앞에 붙이면 변수 이름이 매우 길어집니다. 순전히 충돌 가능성을 0에 가까운 수준으로 줄이기 위한 것입니다.

    이 점에서는 C조차도 C보다 1마일 더 낫습니다. 대부분의 C 프로그램은 주로 함수 지역 변수를 사용하여 작성될 뿐만 아니라 C는 블록 범위 지정도 지원하므로 단일 함수 내의 여러 블록이 교차 오염 없이 변수 이름을 재사용할 수 있습니다.

  4. Shell 프로그래밍 언어에는 표준 라이브러리가 없습니다.

    쉘 스크립팅 언어의 표준 라이브러리는 다음의 내용이라고 PATH말할 수 있습니다.더 강력한언어가 시작됩니다.

    Perl처럼 널리 사용되는 쉘 유틸리티 라이브러리 아카이브도 없습니다.CPAN. 사용 가능한 타사 유틸리티의 대규모 코드 기반이 없으면 프로그래머는 더 많은 코드를 수동으로 작성해야 하므로 생산성이 떨어집니다.

    대부분의 셸 스크립트가 유용한 작업을 수행하기 위해 일반적으로 C로 작성된 외부 프로그램에 의존한다는 사실을 무시하더라도 이 모든 것이 오버헤드를 생성합니다.pipe()fork()exec()콜 체인. 이 모드는 다음과 비교하여 Unix에서 꽤 잘 작동합니다.산업용 컴퓨터다른 운영 체제에서 프로세스를 시작하지만 여기서는서브루틴 호출보다 효율적인 다른 스크립팅 언어를 사용하십시오. 이는 쉘 스크립트 실행 속도의 상한을 심각하게 제한합니다.

  5. 쉘 스크립트에는 병렬 실행을 통해 성능을 향상시키는 몇 가지 내장 기능이 있습니다.

    Bourne 쉘은 &이 목적을 위해 , 및 파이프를 제공하지만 이는 대체로 CPU 또는 I/O 병렬 처리를 달성하기 위한 것이 아니라 여러 프로그램을 작성하는 데에만 적합합니다. wait당신이 할 수 없을 것 같습니다연결하다코어를 사용하거나 쉘 스크립트를 사용하여 RAID 어레이를 포화시키는 경우, 이렇게 하면 다른 언어를 사용하여 더 나은 성능을 얻을 수 있습니다.

    특히 파이프라인은 병렬 실행을 통해 성능을 향상시키는 약한 방법입니다. 두 개의 프로그램만 병렬로 실행할 수 있으며 두 프로그램 중 하나는금지된특정 시점에 서로에 대한 I/O입니다.

    이 문제를 해결하기 위한 최근의 방법은 다음과 같습니다.xargs -P그리고암소 비슷한 일종의 영양parallel, 그러나 이는 위의 4번 항목으로의 이동일 뿐입니다.

    쉘 스크립트는 다중 프로세서 시스템에 내장된 기능을 실제로 최대한 활용하지 못하기 때문에 시스템의 모든 프로세서를 사용할 수 있는 언어로 잘 작성된 프로그램보다 항상 느립니다. GNU Autoconf configure스크립트를 다시 예로 들면, 시스템의 코어 수를 두 배로 늘리는 것은 시스템 실행 속도를 높이는 데 거의 도움이 되지 않습니다.

  6. 쉘 스크립팅 언어는바늘또는인용하다.

    이로 인해 다른 프로그래밍 언어로 쉽게 수행할 수 있는 많은 작업을 수행할 수 없게 됩니다.

    한편으로, 프로그램 메모리에서 다른 데이터 구조를 간접적으로 참조할 수 없다는 것은 내장된 데이터 구조로 제한된다는 것을 의미합니다.데이터 구조. 귀하의 쉘에는연관 배열, 그러나 어떻게 구현됩니까? 여러 가지 가능성이 있으며 각각 서로 다른 장단점이 있습니다.레드 블랙 트리,AVL 트리, 그리고해시 테이블가장 일반적이지만 다른 것들도 있습니다. 서로 다른 절충안이 필요한 경우 참조 없이 여러 유형의 상위 수준 데이터 구조를 수동으로 롤링할 수 없기 때문에 막히게 됩니다. 당신은 주어진 것에 갇혀 있습니다.

    또는 데이터 구조가 필요하지만 쉘 스크립트 인터프리터에 내장된 적절한 대안조차 없을 수도 있습니다.방향성 비순환 그래프, 모델링하는 데 필요할 수도 있습니다.종속성 그래프. 나는 수십 년 동안 프로그래밍을 해왔고 쉘 스크립트에서 이 작업을 수행하기 위해 생각할 수 있는 유일한 방법은 남용하는 것입니다.파일 시스템, 잘못된 참조로 기호 링크를 사용합니다. 이는 솔루션이 우아하거나 빠르거나 이해하기 쉬운지 알 수 없는 튜링 완전성에만 의존할 때 얻을 수 있는 것입니다.

    상위 수준 데이터 구조는 포인터와 참조의 한 가지 용도일 뿐입니다. 가지다수많은 다른 앱이는 Bourne 시리즈 쉘 스크립팅 언어에서는 쉽게 수행할 수 없는 작업입니다.

계속할 수 있지만 요점을 이해하신 것 같습니다. 쉽게 말하면 많다.더 강력한Unix 유형 시스템을 위한 프로그래밍 언어입니다.

이는 어떤 경우에는 언어 자체의 평범함을 보완할 수 있는 큰 이점입니다.

물론, 이것이 바로 GNU Autoconf가 configure스크립트 출력을 위해 의도적으로 제한된 Bourne 쉘 스크립팅 언어 제품군의 하위 집합을 사용하는 이유입니다. 따라서 configure스크립트는 거의 모든 곳에서 실행될 수 있습니다.

이식성이 뛰어난 Bourne 쉘 방언으로 작성하는 것이 실용적이라고 믿는 GNU Autoconf 개발자보다 더 많은 사람을 찾을 수는 없을 것입니다. 그러나 그들 자신의 창작물은 대부분 Perl로 작성되었습니다.m4, 단지 약간의 쉘 스크립팅만이 Autoconf의 것입니다.산출순수한 Bourne 쉘 스크립트입니다. 이것이 "Bourne Identity"라는 개념이 얼마나 유용한지에 대한 의문을 제기하지 않는다면, 나는 어떻게 될지 모르겠습니다.

그렇다면 그러한 프로그램이 얼마나 복잡할 수 있는지에 대한 제한이 있습니까?

튜링 완전성 관찰에서 알 ​​수 있듯이 기술적으로는 그렇지 않습니다.

그러나 이것이 대규모 쉘 스크립트가 작성하기 쉽고, 디버깅하기 쉽고, 실행 속도가 빠르다는 것을 의미하지는 않습니다.

순수 bash에서 파일 압축기/압축 해제기를 작성할 수 있습니까?

PATH"순수한" Bash, ? 압축기는 16진수 이스케이프 시퀀스를 사용할 수 있지만 echo그렇게 하기가 꽤 어렵습니다. 압축 해제기는 다음과 같은 이유로 이런 방식으로 작성될 수 없습니다.셸에서 바이너리 데이터를 처리할 수 없습니다.. 너는 결국 전화할 거야od등을 사용하여 바이너리 데이터를 텍스트 형식으로 변환합니다. 이는 쉘의 기본 데이터 처리 방법입니다.

의도한 대로 쉘 스크립트를 드라이버의 다른 프로그램에 대한 접착제로 사용하는 방법에 대해 이야기하기 시작하면 PATH이제 다른 프로그래밍 언어가 수행할 수 있는 작업이 제한되어 있으므로 제한이 전혀 없기 때문에 문이 열립니다. 다른 프로그램을 호출하여 전체 기능을 얻는 쉘 스크립트는 PATH더 강력한 언어로 작성된 단일 프로그램만큼 빠르게 실행되지는 않습니다.하다달리기.

그게 요점입니다. 빠르게 실행되는 프로그램이 필요하거나 다른 사람의 기능을 빌리는 대신 그 자체로 강력해야 하는 경우 해당 프로그램을 셸에 작성하지 않을 것입니다.

간단한 비디오 게임?

이것은껍질을 벗긴 테트리스. 검색해보시면 다른 비슷한 게임도 있습니다.

매우 제한된 디버깅 도구만 사용 가능

대규모 프로그래밍을 지원하는 데 필요한 기능 목록에서 디버깅 도구 지원 순위는 20위 정도입니다. 많은 프로그래머가 더 많이 의존합니다.printf()디버그언어에 관계없이 적절한 디버거보다 낫습니다.

셸에는 및 가 있으며 echo함께 set -x사용하면 많은 문제를 디버깅하기에 충분합니다.

답변2

우리는 어디에서나 걷거나 수영할 수 있는데 왜 자전거, 자동차, 기차, 보트, 비행기 및 기타 교통 수단을 이용해야 할까요? 물론, 걷거나 수영하는 것은 피곤할 수 있지만 추가 장비가 필요하지 않다는 것은 큰 이점입니다.

한편, bash는 완전한 Turing이지만 정수(너무 크지 않음), 문자열, (1차원) 문자열 배열 및 문자열에서 문자열로의 제한된 매핑 이외의 데이터를 조작하는 데는 좋지 않습니다. 다른 유형의 데이터에는 번거로운 인코딩이 필요하므로 프로그램 작성이 어렵고 실제로 성능이 좋지 않은 경우가 많습니다. 예를 들어, bash의 부동 소수점 연산은 어렵고 느립니다.

게다가 bash는 환경과 상호작용할 수 있는 방법이 거의 없습니다. 프로세스를 실행할 수 있고 리디렉션을 통해 간단한 파일 액세스를 수행할 수 있으며 그게 전부입니다. Bash에는 클라이언트 네트워크 클라이언트도 있습니다. Bash는 쉽게 널 바이트( printf \\0)를 내보낼 수 있지만 입력에서 널 바이트를 구문 분석할 수 없으므로 이진 데이터를 읽는 데 적합하지 않습니다. Bash는 직접적으로 다른 작업을 수행할 수 없습니다. 이를 위해서는 외부 프로그램을 호출해야 합니다. 괜찮습니다. 쉘의 주요 목적은 외부 프로그램을 실행하는 것입니다! Shell은 프로그램을 하나로 묶는 접착제 언어입니다. 그러나 외부 프로그램을 실행하는 경우 이는 해당 프로그램을 사용할 수 있어야 함을 의미합니다. 그러면 이식성의 이점이 줄어듭니다. 어디에서나 사용할 수 있는 몇 가지 프로그램을 고수해야 합니다(대부분POSIX 유틸리티).

을 제외하고는 set -e(유용한) 유형, 네임스페이스, 모듈 또는 중첩된 데이터 구조가 없습니다. 버그는 프로그래밍의 가장 큰 어려움입니다. 버그 없는 프로그램 작성의 용이성이 항상 언어 선택에 결정적인 요소는 아니지만, 이 점에서 bash는 매우 낮은 순위를 차지합니다. Bash는 또한 프로그램을 통합하는 것 이외의 작업을 수행할 때 성능이 좋지 않습니다.

오랫동안 bash는 Windows에서 실행되지 않았으며 오늘날에도 기본 Windows 설치에는 존재하지 않으며 Windows 기본 기능에 대한 인터페이스가 없기 때문에 완전히 기본적으로(WSL에서도) 실행되지 않습니다. Bash는 iOS에서 실행되지 않으며 Android에는 기본적으로 설치되지 않습니다. 따라서 Unix 전용 애플리케이션을 작성하지 않는 한 bash는 전혀 이식성이 없습니다.

컴파일러가 필요한 것은 이식성에 문제가 되지 않습니다. 컴파일러는 개발자의 컴퓨터에서 실행됩니다. 인터프리터나 타사 라이브러리의 필요성이 문제가 될 수 있지만 Linux에서는 배포 패키지를 통해 이 문제가 해결되는 반면, Windows, Android 및 iOS에서는 일반적으로 애플리케이션 패키지에 타사 구성 요소를 번들로 제공합니다. 따라서 고려 중인 이식성 문제는 일반 응용 프로그램의 실제 문제가 아닙니다.

내 대답은 bash 이외의 쉘에 적용됩니다. 일부 세부 사항은 쉘마다 다르지만 일반적인 아이디어는 동일합니다.

답변3

대규모 프로그램에 쉘 스크립트를 사용하지 않는 몇 가지 이유는 다음과 같습니다.

  • 대부분의 기능은 외부 명령을 분기하여 수행되는데 이는 속도가 느립니다. mkdir대조적 으로 Perl과 같은 프로그래밍 언어는 내부적으로 grep.
  • C 라이브러리에 액세스하거나 직접 시스템 호출을 수행하는 쉬운 방법이 없습니다. 이는 비디오 게임을 만들기가 어렵다는 것을 의미합니다.
  • 올바른 프로그래밍 언어는 복잡한 데이터 구조를 더 잘 지원할 수 있습니다. Bash에는 배열과 연관 배열이 있지만 연결된 목록이나 트리에 대해서는 생각하고 싶지 않습니다.
  • 쉘은 텍스트 명령을 처리하는 데 사용됩니다. 이진 데이터(즉, NUL 바이트(값이 0인 바이트)를 포함하는 변수)는 처리하기 어렵거나 심지어 불가능합니다. zsh일부 지원과 함께 비트 쉘에 따라 다릅니다 . 이는 외부 프로그램의 인터페이스가 대부분 텍스트 기반이며 \0구분 기호로 사용되기 때문이기도 합니다.
  • 또한, 외부 명령이 존재하기 때문에 코드와 데이터의 분리가 다소 어렵다. 다른 셸에서 데이터를 참조할 때 발생하는 모든 문제를 목격하세요(예: 실행 중이 bash -c ...거나ssh -c ...

관련 정보