#! / bin / sh vs #! / bin / bash - 최대의 이식성을 제공합니다.

cherouvim 07/30/2017. 3 answers, 2.886 views
linux bash shell sh

나는 대개 우분투 LTS 서버와 함께 작업하는데, symlink /bin/sh/bin/dash . symlink /bin/sh 에서 /bin/bash 까지의 다른 많은 배포판.

나는 스크립트가 #!/bin/sh 를 맨 위에 사용하면 모든 서버에서 같은 방식으로 실행되지 않을 수 있음을 이해합니다.

서버간에 스크립트의 최대 이식성을 원할 때 스크립트에 사용할 쉘을 제안하는 연습이 있습니까?

1 Comments
Thorbjørn Ravn Andersen 08/01/2017
여러 쉘 사이에는 약간의 차이가 있습니다. 이식성이 가장 중요하다면 #!/bin/sh 를 사용하고 원래 쉘이 제공 한 것 이외의 것을 사용하지 마십시오.

3 Answers


Gordon Davisson 08/01/2017.

쉘 스크립트에는 대략 4 단계의 이식성이 있습니다 (세방 (shebang) 라인에 관한 한) :

  1. 대부분의 이식성 : #!/bin/sh only 사용하고 POSIX 표준에 지정된 기본 쉘 구문 only 사용하십시오. 거의 모든 POSIX / unix / linux 시스템에서 작동합니다. (솔라리스 10 이전 버전의 경우, Bourne 쉘을 가지고 있었고, POSIX를 따르지 않았기 때문에 /bin/sh .)

  2. 두번째로 이식성이 뛰어납니다 : #!/bin/bash (또는 #!/usr/bin/env bash )의 shebang 행을 사용하고 bash v3 기능을 고수하십시오. 이것은 예상되는 위치에 bash가있는 시스템에서 작동합니다.

  3. 셋째로 휴대용 : #!/bin/bash (또는 #!/usr/bin/env bash )의 shebang 행을 사용하고 bash v4 기능을 사용하십시오. 이것은 bash v3를 가진 시스템에서 실패 할 것입니다 (예를 들어, 라이센스 이유 때문에 그것을 사용해야하는 macOS).

  4. 최소한의 휴대용 : #!/bin/sh 디렉토리를 사용하고 POSIX 쉘 구문에 대한 bash 확장을 사용하십시오. 이것은 / bin / sh에 대한 bash 이외의 다른 시스템 (예 : 최근 Ubuntu 버전)에서는 실패합니다. 이것을 절대하지 마십시오. 호환성 문제 만있는 것은 아니며 단지 잘못되었습니다. 불행히도, 그것은 많은 사람들이 만드는 오류입니다.

내 추천 : 스크립트에 필요한 모든 셸 기능을 제공하는 처음 세 가지 중에서 가장 보수적 인 방법을 사용하십시오. 최대 이식성을 위해 옵션 # 1을 사용하십시오. 그러나 제 경험상 배열과 같은 몇몇 bash 기능은 # 2와 함께 사용하는 데 도움이됩니다.

네가 할 수있는 가장 최악의 일은 잘못된 전단을 사용하는 # 4이다. 기본 POSIX와 bash 확장 기능이 무엇인지 잘 모르는 경우에는 bash shebang (예 : 옵션 # 2)을 사용하거나 우분투 LTS 서버의 대시와 같은 아주 기본적인 쉘을 사용하여 스크립트를 철저히 테스트하십시오. 우분투 위키에는 조심해야훌륭한 bashisms 목록이 있습니다.

유닉스와 리눅스의 질문에서 쉘의 역사와 차이점에 대한 아주 좋은 정보 가 있습니다. "sh 호환이된다는 것은 무엇을 의미합니까?" Stackoverflow 질문 "sh와 bash의 차이" .

또한 쉘이 다른 시스템마다 다른 유일한 것이 아니라는 점을 알아 두십시오. 리눅스에 익숙하다면, 다른 유닉스 시스템에서는 볼 수없는 비표준 확장이 많이있는 GNU 명령어 (예 : bsd, macOS)에 익숙합니다. 불행히도 여기에는 간단한 규칙이 없습니다. 사용중인 명령의 변형 범위를 알아야합니다.

이식성 측면에서 가장 까다로운 명령 중 하나는 가장 기본적인 : echo 입니다. 어떤 옵션 (예 : echo -n 또는 echo -e )을 사용하거나 인쇄 할 문자열에서 이스케이프 (백 슬래시)를 사용할 때마다 다른 버전이 다른 작업을 수행합니다. 그 뒤의 줄 바꿈이나 문자열의 이스케이프없이 문자열을 인쇄하려면 printf 대신 사용하십시오 (작동 방법을 배우면 echo 보다 복잡합니다). ps 명령어 또한 엉망 입니다.

또 다른 일반적인 일은 명령 옵션 구문에 대한 최근 / GNU 확장입니다. 예전 (표준) 명령 형식은 명령 뒤에 옵션 (단일 대시와 각 옵션이 하나의 문자 임)과 그 다음에 명령 인수. 최근의 (그리고 종종 이식 가능하지 않은) 변형은 긴 옵션 (일반적으로 -로 시작됨)을 포함하고 옵션이 인수 다음에 오는 것을 허용하고 -- 를 사용하여 인수에서 옵션을 분리합니다.

5 comments
12 pabouk 07/30/2017
네 번째 옵션은 단순히 잘못된 생각 일뿐입니다. 사용하지 마십시오.
3 Gordon Davisson 07/30/2017
@pabouk 전적으로 동의합니다. 그래서이 대답을 명확하게하기 위해 편집했습니다.
1 jlliagre 07/30/2017
귀하의 첫 번째 진술은 약간 오해의 소지가 있습니다. POSIX 표준은 바깥 쪽의 세방 (shebang)에 대해 아무 것도 지정하지 않고있다. 게다가 POSIX는 posix 쉘의 위치를 ​​지정하지 않고 그 이름 ( sh )만을 지정하므로 /bin/sh 가 올바른 경로로 보장되지 않습니다. 가장 이식성이있는 곳은 어떤 shebang도 지정하지 않거나 사용 된 OS에 shebang을 적용하는 것이 아닙니다.
2 Michael Kjörling 07/30/2017
저는 최근에 제 스크립트를 사용하여 # 4로 떨어졌고 왜 작동하지 않는지를 알 수 없었습니다. 결국, 같은 명령이 튼튼하게 작동했고, 내가 쉘에서 직접 시도했을 때, 내가하고 싶었던 명령을 정확하게 수행했습니다. #!/bin/sh#!/bin/bash 로 변경하자마자 스크립트는 완벽하게 작동했습니다. (내 방어를 위해, 그 스크립트는 실제로 sh-isms가 필요한 것에서부터 bash와 같은 행동에 의존하는 것으로 진화했다.)
2 jlliagre 07/30/2017
@ MichaelKjörling (진정한) Bourne 쉘은 거의 Linux 배포판에 번들로 제공되지 않으며 어쨌든 POSIX를 준수하지 않습니다. POSIX 표준 셸은 Bourne이 아닌 ksh 동작으로 만들어졌습니다. FHS를 따르는 대부분의 리눅스 배포판은 POSIX 호환성을 제공하기 위해 선택한 쉘에 대한 심볼릭 링크 인 /bin/sh 를 가지고 있습니다. 일반적으로 bash 또는 dash 됩니다.

Kaz 07/31/2017.

TXR 언어를 작성하기 위해 준비한 ./configure 스크립트에서 더 나은 이식성을 위해 다음 프롤로그를 작성했습니다. #!/bin/sh 가 POSIX가 아닌 이전 Bourne 쉘인 경우에도 스크립트는 자체적으로 부트 스트랩을 수행합니다. (필자는 Solaris 10 VM에서 모든 릴리스를 빌드합니다.)

#!/bin/sh

# use your own variable name instead of txr_shell;
# adjust to taste: search for your favorite shells

if test x$txr_shell = x ; then
  for shell in /bin/bash /usr/bin/bash /usr/xpg4/bin/sh ; do
    if test -x $shell ; then
       txr_shell=$shell
       break
    fi
  done
  if test x$txr_shell = x ; then
    echo "No known POSIX shell found: falling back on /bin/sh, which may not work"
    txr_shell=/bin/sh
  fi
  export txr_shell
  exec $txr_shell $0 ${@+"$@"}
fi

# rest of the script here, executing in upgraded shell 

여기서 생각하는 것은 우리가 실행중인 쉘보다 더 나은 쉘을 찾고 그 쉘을 사용하여 스크립트를 다시 실행한다는 것입니다. txr_shell 환경 변수가 설정되어 재실행 된 스크립트가 재실행 된 재귀 인스턴스라는 것을 알 수 있습니다.

(필자의 스크립트에서는 txr_shell 변수가 정확히 두 가지 용도로 사용 txr_shell , 첫 번째는 스크립트 출력에 유익한 메시지의 일부로 인쇄되고 둘째는 Makefile SHELL 변수로 설치되므로 make 는이 셸을 사용하여 요리법을 실행합니다.)

/bin/sh 가 대시 인 시스템에서 위의 로직이 /bin/bash 찾아서 그 스크립트를 다시 실행한다는 것을 알 수 있습니다.

Solaris 10 상자에서는 Bash가 발견되지 않으면 /usr/xpg4/bin/sh 가 시작됩니다.

프롤로그는 파일 존재 test 위한 test 를 사용하여 보수적 인 쉘 언어로 작성되었고, 깨진 이전 쉘에 대한 인수 확장을위한 ${@+"$@"} 트릭입니다 (우리가 있었다면 간단히 "$@" 가됩니다). POSIX 호환 쉘에서).

2 comments
Charles Duffy 07/31/2017
적절한 인용이 사용된다면 x hackery가 필요하지 않을 것입니다. 왜냐하면이 관용구를 사용하는 상황은 -a 또는 -o 하여 여러 테스트를 조합하여 현재 사용되지 않는 테스트 호출을 둘러싸고 있기 때문입니다.
Kaz 07/31/2017
@CharlesDuffy 사실; 내가 거기서 범행하고있는 test x$whatever 바니시에 양파처럼 보인다. 깨진 오래된 셸이 따옴표를 사용한다고 믿을 수 없다면, 마지막 ${@+"$@"} 시도는 무의미합니다.

zwol 07/31/2017.

Bourne 셸 언어의 모든 변형은 Perl, Python, Ruby, node.js 및 심지어 Tcl과 같은 최신 스크립팅 언어와 비교할 때 객관적으로 끔찍합니다. 조금이라도 복잡한 작업을해야한다면 쉘 스크립트 대신 위의 스크립트 중 하나를 사용하면 장기적으로 더 행복해집니다.

셸 언어가 여전히 새로운 언어에 비해 갖는 유일한 장점은 /bin/sh 호출하는 것이 유닉스라는 것을 의미하는 모든 것에 존재한다는 것입니다. 그렇지만 POSIX와 호환되지 않을 수도 있습니다. 기존 독점 Unix의 많은 사람들은 Unix95 (예, Unix95, 20 년 전에 계산)에 의해 요구 된 변경 prior/bin/sh 의해 구현 된 언어와 기본 PATH에있는 유틸리티를 동결 시켰습니다. 운이 좋다면 Unix95 또는 심지어 POSIX.1-2001 세트가있을 수도 있지만, 기본 PATH (예 : /usr/xpg4/bin )에 not 디렉토리의 도구이지만 존재하지 않을 수도 있습니다.

그러나 Perl의 기본은 Bash가 선택한 임의의 UNIX 설치에 more likelymore likely . ( "Perl의 기초"라는 말은 /usr/bin/perl 이 존재하고 Perl 5의 some 버전 일 가능성이 some , 운이 좋다면 그 버전의 인터프리터와 함께 제공되는 모듈 세트도 있습니다. 유효한.)

따라서:

Unix가되는 everywhere 에서 작동해야하는 무언가 ( "configure"스크립트와 같은)를 쓰고 있다면, #! /bin/sh #! /bin/sh 이고 어떤 확장도 사용하지 않아야합니다. 요즘에는이 상황에서 POSIX.1-2001을 준수하는 쉘을 작성 하겠지만 누군가 녹슨 철에 대한 지원을 요청하면 POSIX를 패치 할 준비가됩니다.

그러나 어디서나 작동해야하는 무언가를 쓰지 않는다면 Bashism을 전혀 사용하지 않으려 고하는 순간 더 나은 스크립팅 언어로 전체 작업을 중단하고 다시 작성해야합니다. 너의 미래는 너에게 고맙다.

(Bash 확장 기능을 사용하는 것이 적절한 경우 is 언제입니까? 첫 번째 순서로 : 결코. 두 번째 순서로 : Bash 인터랙티브 환경을 확장하는 것 - 예 : 스마트 탭 완성 및 멋진 프롬프트 제공.)

Related questions

Hot questions

Language

Popular Tags