"#! / path / to / NAME"대신 "#! / usr / bin / env NAME"을 내 방아쇠로 사용하는 것이 더 좋은 이유는 무엇입니까?


458

나는 다른 사람들로부터 얻은 일부 스크립트는 shebang #!/path/to/NAME을 가지고 있고 다른 스크립트 는 같은 도구를 사용하여 shebang 을 가지고 있음을 알았습니다 #!/usr/bin/env NAME.

둘 다 제대로 작동하는 것 같습니다. 자습서 (예 : Python)에서는 후자의 shebang이 더 낫다는 제안이있는 것 같습니다. 그러나 이것이 왜 그런지 잘 모르겠습니다.

나는 후자의 shebang을 사용하려면 NAME이 PATH에 있어야하지만 첫 번째 shebang에는이 제한이 없다는 것을 알고 있습니다.

또한 NAME이 위치한 곳을 정확하게 지정하기 때문에 첫 번째가 더 나은 Shebang이 될 것입니다. 따라서이 경우 여러 버전의 NAME (예 : / usr / bin / NAME, / usr / local / bin / NAME)이있는 경우 첫 번째 경우는 사용할 버전을 지정합니다.

내 질문은 왜 첫 번째 세방이 두 번째 세방보다 선호됩니까?


12
이 답변을 참조하십시오 ...
jasonwryan

@ TheGeeko61 : 내 경우에는 무언가가 깨졌고 일부 변수는 env에 없었습니다. 따라서이 shebang을 사용하여 env가 올바르게로드되었는지 확인하는 것이 좋습니다.
Gigamegs 2016 년

답변:


462

반드시 더 좋은 것은 아닙니다.

장점은 사용자의 첫 번째 실행 파일 #!/usr/bin/env python을 모두 사용한다는 것입니다 .python$PATH

단점 의는 #!/usr/bin/env python그것이 무엇이든 사용하는 것입니다 python사용자의에 처음 나타나는 실행 $PATH.

이는 스크립트를 실행하는 사람에 따라 스크립트가 다르게 작동 할 수 있음을 의미합니다. 한 사용자의 /usr/bin/python경우 OS와 함께 설치된 것을 사용할 수 있습니다 . 다른 경우에는 /home/phred/bin/python제대로 작동하지 않는 실험 을 사용할 수 있습니다 .

만약 python단지에 설치되어 /usr/local/bin,이없는 사용자 /usr/local/bin인은 $PATH심지어 스크립트를 실행할 수 없습니다. (아마도 현대 시스템에서는 그럴 것 같지 않지만 더 모호한 통역사에게는 쉽게 발생할 수 있습니다.)

지정 #!/usr/bin/python하면 특정 시스템 에서 스크립트를 실행하는 데 사용할 인터프리터를 정확하게 지정할 수 있습니다 .

또 다른 잠재적 인 문제는 #!/usr/bin/env트릭 으로 인해 해석자가 인수를 전달할 수 없다는 것입니다 (암시 적으로 전달되는 스크립트 이름 이외). 이것은 일반적으로 문제가 아니지만 문제가 될 수 있습니다. 많은 Perl 스크립트는로 작성 #!/usr/bin/perl -w되었지만 use warnings;요즘에는 권장되는 대체 방법입니다. Csh 스크립트는 사용해야 #!/bin/csh -f하지만 처음 에는 csh 스크립트가 권장되지 않습니다 . 그러나 다른 예가있을 수 있습니다.

개인 시스템 제어 시스템에 새 시스템에 계정을 설정할 때 설치하는 여러 Perl 스크립트가 있습니다. 내 스크립트를 #!설치할 때 각 스크립트 의 줄 을 수정하는 설치 관리자 스크립트를 사용합니다 $HOME/bin. (저는 #!/usr/bin/perl최근에 다른 것을 사용할 필요가 없었습니다 . Perl이 기본적으로 설치되지 않은 때로 되돌아갑니다.)

사소한 요점 : #!/usr/bin/env트릭은 아마도 env이름이 암시하는 것처럼 변경된 환경에서 명령을 호출하기위한 명령 의 남용입니다 . 또한 일부 구형 시스템 (정확하게 리콜하면 SunOS 4 포함)에 env명령 이 없었 습니다 /usr/bin. 이들 중 어느 것도 중요한 문제가되지 않을 것입니다. env이 방법으로 작동하면 많은 스크립트가 #!/usr/bin/env트릭을 사용 하며 OS 제공 업체는이를 해결하기 위해 아무것도하지 않을 것입니다. 그것은 수도 당신이 당신의 스크립트가 정말 오래된 시스템에서 실행하려면,하지만 당신은 어쨌든 그것을 수정해야 할 가능성이있는 경우 문제가 될 수.

또 다른 가능한 문제는 의견에서 지적한 Sopalajo de Arrierez 덕분에 크론 작업이 제한된 환경에서 실행된다는 것입니다. 특히, $PATH일반적으로 같은 것 /usr/bin:/bin입니다. 따라서 인터프리터를 포함하는 디렉토리가 해당 디렉토리 중 하나에 있지 않으면 $PATH사용자 쉘 의 기본값 에 있더라도 /usr/bin/env트릭이 작동하지 않습니다. 정확한 경로를 지정하거나 crontab에 줄을 추가하여 설정할 수 있습니다 $PATH( man 5 crontab자세한 내용).


4
/ usr / bin / perl이 perl 5.8이고 $ HOME / bin / perl이 5.12이고 shebang에 5.12 하드 코드 / usr / bin / perl이 필요한 스크립트 인 경우 스크립트를 실행하는 데 큰 고통이 될 수 있습니다. PATH에서 / usr / bin / env perl perl perl이 문제가되는 것을 거의 보지 못했지만 종종 매우 유용합니다. 그리고 그것은 exec hack보다 훨씬 더 예쁘다!
윌리엄 퍼셀

8
특정 인터프리터 버전을 사용하려는 경우 / usr / bin / env가 여전히 낫다는 점은 주목할 가치가 있습니다. 일반적으로이 있기 때문에 단순히 있는 등 perl5의, perl5.12, perl5.10, python3.3라는 이름의 기계, python3.32에와 앱이 해당 특정 버전에서 테스트 된 경우가 여러 개 설치 인터프리터 버전, 당신은 여전히 지정할 수 있습니다 #! / usr / bin / env perl5.12이며 사용자가 비정상적인 위치에 설치 한 경우에도 괜찮습니다. 내 경험상 '파이썬'은 일반적으로 시스템 표준 버전에 대한 심볼릭 링크입니다 (시스템의 최신 버전 일 필요는 없음).
root

6
@root : Perl 에게 그 목적 중 일부use v5.12;제공합니다 . 그리고 시스템이 5.12 펄 5.14이 아닌 경우 실패합니다. 파이썬 2 대 3의 경우 와 일 가능성이 높다. #!/usr/bin/env perl5.12#!/usr/bin/python2#!/usr/bin/python3
키이스 톰슨

3
@GoodPerson :에 설치할 Perl 스크립트를 작성한다고 가정하십시오 /usr/local/bin. 나는 그것이 올바르게 작동한다는 것을 알고 /usr/bin/perl있습니다. 나는 임의의 임의의 사용자가 자신에게 가지고있는 실행 파일 이 무엇이든 작동하는지 전혀 모른다 . 아마도 누군가 고대 버전의 Perl을 실험하고있을 것입니다. 내가 지정했기 때문에 내 스크립트 (사용자가 반드시 알지 못하거나 관심이있는 Perl 스크립트 임)가 작동하지 않습니다. perl$PATH#!/usr/bin/perl
Keith Thompson

5
작동하지 않는 경우 /usr/bin/perl매우 빨리 알아볼 수 있으며 최신 상태를 유지하는 것은 시스템 소유자 / 관리자의 책임입니다. 자신의 펄로 내 스크립트를 실행하려면 자유롭게 사본을 잡고 수정하거나를 통해 호출하십시오 perl foo. (그리고 당신은이 답변을 찬성 한 55 명의 사람들도 한두 가지를 알고있을 가능성을 고려할 것입니다. 당신이 옳고 모두 틀렸을 가능성은 있지만, 그것은 내기 방식이 아닙니다.)
Keith 톰슨

101

/ usr / bin / env가를 해석 할 수 있기 때문에 $PATH스크립트를보다 이식성이있게 만듭니다.

#!/usr/local/bin/python

파이썬이 / usr / local / bin에 설치된 경우에만 스크립트를 실행합니다.

#!/usr/bin/env python

를 해석 $PATH하고의 모든 디렉토리에서 파이썬을 찾으십시오 $PATH.

따라서 스크립트는 이식성이 뛰어나고 python이 /usr/bin/python, 또는 /usr/local/bin/python, 또는 사용자 정의 디렉토리 (에 추가 된 $PATH) 로 설치된 시스템에서 수정하지 않고도 작동 /opt/local/bin/python합니다.

이식성이 env하드 코딩 된 경로보다 선호되는 유일한 이유 입니다.


19
python실행 파일의 사용자 정의 디렉토리는 virtualenv사용량이 증가함에 따라 특히 일반적 입니다.
Xiong Chiamiov 2018

1
어떻습니까 #! python, 왜 사용되지 않습니까?
kristianp

6
#! python베어 워드 python는 파일의 전체 경로로 해석 되므로 파이썬 바이너리와 동일한 디렉토리에 있어야하기 때문에 사용되지 않습니다 . 현재 디렉토리에 파이썬 바이너리가 없으면 다음과 같은 오류가 발생 bash: ./script.py: python: bad interpreter: No such file or directory합니다. 마치 사용했던 것과 같습니다#! /not/a/real/path/python
Tim Kennedy

47

주어진 시스템에서 절대 경로를 지정하는 것이 더 정확합니다. 단점은 너무 정확하다는 것입니다. Perl의 시스템 설치가 스크립트에 비해 너무 오래되었고 대신 직접 사용하고 싶다고 가정하면 스크립트를 편집하고로 변경 #!/usr/bin/perl해야 #!/home/myname/bin/perl합니다. 더 나쁜 점 /usr/bin은 일부 컴퓨터, 다른 컴퓨터 /usr/local/bin및 다른 컴퓨터 에서 Perl을 사용 하는 경우 /home/myname/bin/perl세 개의 별도 스크립트 사본을 유지 관리하고 각 컴퓨터에서 적절한 스크립트를 실행해야합니다.

#!/usr/bin/env나쁘면 깨지지 PATH만 거의 아무것도하지 않습니다. 나쁜 작업을 시도하는 PATH것은 매우 유용하지 않으며 스크립트가 실행되는 시스템에 대해 거의 알지 못하므로 절대 경로에 의존 할 수 없습니다.

거의 모든 유닉스 변형에 의존 할 수있는 두 개의 프로그램이 있습니다 : /bin/sh/usr/bin/env. 애매 모호하고 은퇴 한 유닉스 변종 중 일부는 /bin/env을 갖지 /usr/bin/env못했지만 그럴 가능성은 거의 없습니다. 현대 시스템은 /usr/bin/env셰방에서 널리 사용되기 때문에 정확하게 사용되었습니다. /usr/bin/env믿을 수있는 것입니다.

을 제외 /bin/sh하고 shebang에서 절대 경로를 사용해야하는 유일한 경우는 스크립트가 이식성이 없어야하는 것으로 통역사의 알려진 위치를 신뢰할 수 있습니다. 예를 들어 Linux에서만 작동하는 bash 스크립트는 안전하게 사용할 수 있습니다 #!/bin/bash. 사내에서만 사용되는 스크립트는 하우스 통역사 위치 규칙에 의존 할 수 있습니다.

#!/usr/bin/env단점이 있습니다. 절대 경로를 지정하는 것보다 유연하지만 여전히 통역사 이름을 알아야합니다. 경우에 따라 ( $PATH예 : 스크립트와 관련된 위치에 없는) 인터프리터를 실행하려고 할 수 있습니다 . 이러한 경우 표준 쉘과 원하는 인터프리터에서 모두 해석 할 수 있는 폴리 글 로트 스크립트 를 만들 수 있습니다. 예를 들어, Python 2 스크립트를 pythonPython 3 및 python2Python 2 인 시스템과 Python 2 인 시스템 pythonpython2존재하지 않는 시스템 모두에서 이식 가능하게하려면 다음을 수행하십시오.

#!/bin/sh
''':'
if type python2 >/dev/null 2>/dev/null; then
  exec python2 "$0" "$@"
else
  exec python "$0" "$@"
fi
'''
# real Python script starts here
def …

22

특히 perl의 경우 #!/usr/bin/env두 가지 이유로 사용하는 것이 좋지 않습니다.

첫째, 휴대용이 아닙니다. 일부 모호한 플랫폼에서 env는 / usr / bin에 없습니다. 둘째, 키이스 톰슨 (Keith Thompson) 이 지적했듯이 shebang 라인에 인수를 전달하는 데 문제가 발생할 수 있습니다. 최대 휴대용 솔루션은 다음과 같습니다.

#!/bin/sh
exec perl -x "$0" "$@"
#!perl

작동 방식에 대한 자세한 내용은 'perldoc perlrun'및 -x 인수에 대한 내용을 참조하십시오.


정확히 내가 찾은 anser : perl에 추가 aruments를 전달할 수있는 perl 스크립트에 대해 가능한 "shebang"을 작성하는 방법 (이 예제의 마지막 줄은 추가 aruments를 허용합니다).
AmokHuginnsson

15

이 둘이 구별되는 이유는 스크립트가 실행되는 방식 때문입니다.

사용하는 것이 /usr/bin/env(다른 답변에서 언급 한 바와 같이,에서하지 않은, /usr/bin그냥 한 후 실행 파일 이름을 넣을 수 없기 때문에 어떤 운영체제에) 필요 #!가 절대 경로 여야합니다 -. #!메커니즘이 쉘보다 낮은 레벨에서 작동 하기 때문 입니다. 커널 바이너리 로더의 일부입니다. 테스트 할 수 있습니다. 이것을 파일에 넣고 실행 파일로 표시하십시오.

#!bash

echo 'foo'

실행하려고 할 때 다음과 같은 오류가 인쇄됩니다.

Failed to execute process './test.sh'. Reason:
The file './test.sh' does not exist or could not be executed.

파일이 실행 파일로 표시되고로 시작하는 #!경우 커널 (알 수없는 $PATH디렉토리 또는 현재 디렉토리 : 사용자 랜드 개념)은 절대 경로를 사용하여 파일을 찾습니다. 절대 경로를 사용하는 것은 문제가 있기 때문에 (다른 답변에서 언급했듯이) 누군가가 트릭을 생각해 냈습니다. /usr/bin/env(거의 항상 해당 위치에 있음)을 사용하여 무언가를 실행할 수 $PATH있습니다.


11

사용에 두 가지 문제가 더 있습니다 #!/usr/bin/env

  1. 인터프리터의 전체 경로를 지정하는 문제를 해결하지 않고 단지로 이동합니다 env.

    env에 있거나 파이썬에 있다고 보장 된 /usr/bin/env것보다 더 이상 보장되지 않습니다 .bash/bin/bash/usr/bin/python

  2. env ARGV [0]을 인터프리터 이름으로 덮어 씁니다 (예 : bash 또는 python).

    이렇게하면 스크립트 이름이 ps출력 (또는 출력 방법 / 위치 변경)에 표시되지 않으며 예를 들어ps -C scriptname.sh

[업데이트 2016-06-04]

그리고 세번째 문제 :

  1. PATH 변경 은 스크립트의 첫 번째 줄을 편집하는 것보다 더 많은 작업입니다. 특히 이러한 편집을 스크립팅하는 것이 쉽지 않습니다. 예 :

    printf "%s\n" 1 i '#!'$(type -P python2) . w | ed foo.py

    디렉토리를 $ PATH에 추가하거나 미리 추가하는 것은 상당히 쉽습니다 (파일을 영구적으로 만들기 위해 파일을 편집해야하지만 자신 ~/.profile이나 무엇이든). , 첫 번째 줄이 아님).

    PATH 디렉토리의 순서를 변경하는 것은 #!라인을 편집하는 것보다 훨씬 어렵고 어렵습니다 .

    그리고 당신은 여전히 ​​사용하는 다른 모든 문제가 #!/usr/bin/env있습니다.

    @jlliagre는 #!/usr/bin/env여러 개의 인터프리터 버전으로 스크립트를 테스트하는 데 유용한 주석 에서 "PATH / PATH 순서 만 변경하여"

    그렇게 해야하는 경우 #!스크립트 상단에 여러 줄을 두는 것이 훨씬 쉽고 (첫 번째 줄 이외의 주석 만 표시합니다) 지금 사용하려는 줄을 잘라 내고 복사하여 붙여 넣기 가 더 쉽습니다. 첫 줄.

    에서 vi, 그것은으로 커서를 이동하는 것처럼 간단 할 것이다 #!다음, 당신이 원하는 라인 입력 dd1GP또는 Y1GP. 과 같은 편집기 nano를 사용하더라도 마우스를 사용하여 복사하여 붙여 넣는 데 몇 초가 걸립니다.


전반적으로, 사용의 장점 #!/usr/bin/env은 최소한이며, 단점을 능가하는 것은 아닙니다. "편의"의 장점조차도 환상적입니다.

IMO는 운영 체제가 작업 대상이 아니라고 생각하거나 문제해결 하는 데 문제 가 있다고 생각하는 특정 종류의 프로그래머가 권장하는 어리석은 아이디어 입니다.

추신 : 여기에 여러 파일의 인터프리터를 한 번에 변경하는 간단한 스크립트가 있습니다.

change-shebang.sh:

#!/bin/bash

interpreter="$1"
shift

if [ -z "$(type -P $interpreter)" ] ; then
  echo "Error: '$interpreter' is not executable." >&2
  exit 1
fi

if [ ! -d "$interpreter" ] && [ -x "$interpreter" ] ; then
  shebang='#!'"$(realpath -e $interpreter)" || exit 1
else
  shebang='#!'"$(type -P $interpreter)"
fi

for f in "$@" ; do
  printf "%s\n" 1 i "$shebang" . w | ed "$f"
done

예를 들어, change-shebang.sh python2.7 *.py또는change-shebang.sh $HOME/bin/my-experimental-ruby *.rb


4
여기서 다른 사람은 ARGV [0] 문제를 언급하지 않았습니다. 그리고 아무도 / path / to / env 문제를 사용하기위한 인수 중 하나를 직접 다루는 형식으로 언급하지 않았습니다 (예 : bash 또는 perl이 예기치 않은 위치에있을 수 있음).
cas

2
인터프리터 경로 문제는 심볼릭 링크가있는 sysadmin이나 스크립트를 편집하는 사용자가 쉽게 해결할 수 있습니다. 여기에 언급 된 shebang 줄과 다른 질문에 env를 사용하여 발생하는 다른 모든 문제를 사람들이 따라 잡을 수 있도록 장려하는 것은 확실히 중요한 문제는 아닙니다. 그것은 csh스크립팅을 장려하는 것이 나쁜 해결책 을 장려하는 것과 같은 방식 으로 나쁜 해결책을 조장하는 것입니다. 그것은 일종의 작품이지만 훨씬 더 좋은 대안이 있습니다.
cas

3
비 sysadmin은 sysadmin에게 요청할 수 있습니다. 또는 단순히 스크립트를 편집하고 #!행을 변경할 수 있습니다.
cas

2
그것은 python과 관련이없는 sysadmin 또는 OS 특정 기술 지식에 따라 env가 피하는 데 정확히 도움이됩니다.
jlliagre

1
누군가가 사용 하고 로컬에서 제거해야하거나 사용하지 않았거나 추가 해야하는 경우 실제로 어느 쪽이든 큰 대답이기 때문에 수천 번 투표 할 수 있기 /usr/bin/env를 바랍니다. 두 경우 모두 발생할 수 있으므로이 답변에 제공된 스크립트는 도구 상자에 잠재적으로 유용한 도구입니다.
jstine

8

여기에 다른 예를 추가하십시오.

예를 들어 env여러 rvm환경 간에 스크립트를 공유하려는 경우에도 사용 이 유용합니다 .

cmd 행에서 이것을 실행하면 #!/usr/bin/env ruby스크립트 내에서 사용될 때 어떤 루비 버전이 사용될지를 보여줍니다 .

env ruby --version

따라서를 사용할 때 env스크립트를 변경하지 않고 rvm을 통해 다른 루비 버전을 사용할 수 있습니다.


1
// 훌륭한 아이디어입니다. 여러 인터프리터의 전체 포인트는 암호를 해독하거나 코드에 의존해야하는 아닙니다 특정 인터프리터 .
Nathan Basanese

4

자신이나 직장을 위해 순수하게 글을 쓰고 있고 통역사의 위치가 항상 같은 곳에 있다면 반드시 직접 경로를 사용하십시오. 다른 모든 경우에는을 사용하십시오 #!/usr/bin/env.

이유는 다음과 같습니다. python사용하는 구문에 관계없이 통역사가 동일한 위치에 있었지만 많은 사람들이 다른 곳에 설치했을 수 있습니다. 대부분의 주요 프로그래밍 인터프리터는 /usr/bin/많은 최신 소프트웨어 에 있지만 기본값은 /usr/local/bin/입니다.

#!/usr/bin/env동일한 인터프리터의 여러 버전이 설치되어 있고 쉘의 기본값을 모르는 경우 항상 수정해야하기 때문에 항상 사용한다고 주장 합니다.


5
"다른 모든 경우에는 #! / usr / bin / env를 사용하십시오"가 너무 강합니다. // @KeithThompson과 다른 사람들이 지적했듯이 #! / usr / bin / env는 스크립트가 누가 / 어떻게 실행되는지에 따라 다르게 동작 할 수 있음을 의미합니다. 때때로이 다른 동작은 보안 버그 일 수 있습니다. // 예를 들어 setuid 또는 setgid 스크립트에 "#! / usr / bin / env python"을 사용하지 마십시오. 스크립트를 호출하는 사용자는 PATH의 "python"이라는 파일에 맬웨어를 배치 할 수 있습니다. setuid / gid 스크립트가 좋은 아이디어인지 여부는 다른 문제입니다. 그러나 setuid / gid 실행 파일이 사용자가 제공 한 환경을 신뢰해서는 안됩니다.
Krazy Glew

@KrazyGlew, Linux에서는 스크립트를 설정할 수 없습니다. 실행 파일을 통해 수행합니다. 이 실행 파일을 작성할 때 환경 변수를 지우는 것이 좋습니다. 일부 환경 변수도 의도적으로 무시 됩니다.
MayeulC

3

이식성과 호환성을 위해 사용하는 것이 좋습니다

#!/usr/bin/env bash

대신에

#!/usr/bin/bash

바이너리는 Linux / Unix 시스템에 위치 할 수있는 여러 가능성이 있습니다. 파일 시스템 계층에 대한 자세한 설명은 hier (7) 맨 페이지를 확인하십시오.

예를 들어 FreeBSD는 기본 시스템의 일부가 아닌 모든 소프트웨어를 / usr / local /에 설치 합니다. bash는 기본 시스템의 일부가 아니므로 bash 바이너리는 / usr / local / bin / bash에 설치 됩니다.

대부분의 Linux 배포판과 FreeBSD에서 실행되는 이식 가능한 bash / csh / perl / whatever 스크립트를 원할 경우 #! / usr / bin / env 를 사용해야합니다 .

또한 대부분의 리눅스 설치는 (하드)도 가지고있는 ENV 바이너리를 연결하는 것이, 메모를해야 / 빈에 / 빈 / ENV 또는 softlinked는 / usr / bin으로 하지 오두막에 사용. 따라서 #! / bin / env를 사용하지 마십시오 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.