Programing

사람들은 왜 파이썬 스크립트의 첫 줄에 #! / usr / bin / env python shebang을 작성합니까?

lottogame 2020. 9. 28. 07:51
반응형

사람들은 왜 파이썬 스크립트의 첫 줄에 #! / usr / bin / env python shebang을 작성합니까?


그 줄없이 파일이 똑같이 실행되는 것 같습니다.


여러 버전의 Python이 설치되어있는 경우 /usr/bin/env사용 된 인터프리터가 환경의 $PATH. 대안은 다음과 같이 하드 코딩하는 것입니다 #!/usr/bin/python. 괜찮지 만 유연성이 떨어집니다.

Unix에서 해석 할 실행 파일 #!은 첫 번째 줄의 시작 부분에를 표시하고 그 뒤에 인터프리터 (및 필요한 플래그) 를 사용하여 사용할 인터프리터를 나타낼 수 있습니다 .

당신이 다른 플랫폼에 대해 얘기하는 경우, 물론,이 규칙이 적용되지 않습니다 (하지만 "오두막 라인은"아무런 해를 끼치 지 않으며, 혹시 플랫폼에 해당 스크립트를 복사하면 도움이 될 것입니다 같은 리눅스, 맥 등의 유닉스 기반, 등).


그것을 shebang 라인 이라고합니다 . 현상태대로 위키 백과 항목을 설명합니다 :

컴퓨팅에서 shebang (hashbang, hashpling, pound bang 또는 crunchbang이라고도 함)은 "#!"문자를 나타냅니다. 인터프리터 지시문에서 텍스트 파일의 첫 줄로 처음 두 문자 일 때. Unix와 유사한 운영 체제에서 프로그램 로더는 파일이 스크립트라는 표시로이 두 문자의 존재를 취하고 파일의 나머지 첫 줄에 지정된 인터프리터를 사용하여 해당 스크립트를 실행하려고합니다.

Unix FAQ 항목을 참조하십시오 .

shebang 라인이 실행할 인터프리터를 결정하지 않는 Windows에서도 shebang 라인에 옵션을 지정하여 인터프리터에게 옵션을 전달할 수 있습니다. 일회성 스크립트 (예 : SO에 대한 질문에 답할 때 작성하는 스크립트)에 일반적인 shebang 라인을 유지하는 것이 유용하므로 Windows와 ArchLinux 모두에서 빠르게 테스트 할 수 있습니다 .

ENV 유틸리티를 사용하면 경로에 명령을 호출 할 수 있습니다 :

첫 번째 나머지 인수는 호출 할 프로그램 이름을 지정합니다. PATH환경 변수 에 따라 검색됩니다 . 나머지 인수는 해당 프로그램에 인수로 전달됩니다.


다른 답변에 대해 조금 확장하면 다음은 /usr/bin/envshebang 줄 을 부주의하게 사용하여 명령 줄 스크립트가 어떻게 문제에 빠질 수 있는지에 대한 간단한 예입니다 .

$ /usr/local/bin/python -V
Python 2.6.4
$ /usr/bin/python -V
Python 2.5.1
$ cat my_script.py 
#!/usr/bin/env python
import json
print "hello, json"
$ PATH=/usr/local/bin:/usr/bin
$ ./my_script.py 
hello, json
$ PATH=/usr/bin:/usr/local/bin
$ ./my_script.py 
Traceback (most recent call last):
  File "./my_script.py", line 2, in <module>
    import json
ImportError: No module named json

json 모듈은 Python 2.5에 존재하지 않습니다.

이러한 종류의 문제를 방지하는 한 가지 방법은 일반적으로 대부분의 Python과 함께 설치되는 버전이 지정된 Python 명령 이름을 사용하는 것입니다.

$ cat my_script.py 
#!/usr/bin/env python2.6
import json
print "hello, json"

Python 2.x와 Python 3.x를 구분하기 만하면되는 경우 최신 Python 3 릴리스에서도 python3이름을 제공합니다 .

$ cat my_script.py 
#!/usr/bin/env python3
import json
print("hello, json")

파이썬 스크립트를 실행하기 위해서는 쉘에 세 가지를 알려야합니다.

  1. 파일이 스크립트인지
  2. 스크립트를 실행할 인터프리터
  3. 해당 통역사의 경로

shebang은 #!달성합니다 (1.). shebang은 문자가 많은 스크립팅 언어에서 주석 표시 자 #이기 때문에 a로 시작 #합니다. 따라서 인터프리터는 shebang 줄의 내용을 자동으로 무시합니다.

env명령 수행 (2)와 (3). "grawity"를 인용하면

env명령 의 일반적인 사용은 env가 실행하라는 명령에 대해 $ PATH를 검색한다는 사실을 활용하여 인터프리터를 실행하는 것입니다. shebang 라인은 절대 경로를 지정해야하고 다양한 인터프리터 (perl, bash, python)의 위치가 많이 다를 수 있으므로 다음을 사용하는 것이 일반적입니다.

#!/usr/bin/env perl  / bin / perl, / usr / bin / perl, / usr / local / bin / perl, / usr / local / pkg / perl, / fileserver / usr / bin / perl 또는 / home인지 추측하는 대신 사용자 시스템의 / MrDaniel / usr / bin / perl ...

반면에 env는 거의 항상 / usr / bin / env에 있습니다. (그렇지 않은 경우를 제외하고, 일부 시스템은 / bin / env를 사용할 수 있지만 이는 매우 드문 경우이며 Linux가 아닌 시스템에서만 발생합니다.)


아마도 귀하의 질문은 다음과 같습니다.

사용하려는 경우 : $python myscript.py

그 라인은 전혀 필요하지 않습니다. 시스템이 python을 호출하면 python 인터프리터가 스크립트를 실행합니다.

그러나 사용하려는 경우 : $./myscript.py

정상적인 프로그램이나 bash는 스크립트처럼 직접 호출, 당신은 그것을 실행하는 프로그램을 사용하는 시스템에 지정하는 그 라인을 쓰기 (도로가 실행 만들 필요 chmod 755)


기술적으로 파이썬에서 이것은 단지 주석 라인입니다.

이 줄은 (명령 줄)에서 py 스크립트를 실행하는 경우에만 사용됩니다 . 이것은 " Shebang !"으로 알려져 있습니다 . 이며 Python 스크립트뿐만 아니라 다양한 상황에서 사용됩니다.

여기서는 파일의 나머지 부분을 처리하기 위해 특정 버전 의 Python 을 시작하도록 셸에 지시 합니다.


이렇게하는 주된 이유는 운영 체제 환경에서 스크립트를 이식 가능하게 만드는 것입니다.

예를 들어 mingw에서 python 스크립트는 다음을 사용합니다.

#!/c/python3k/python 

GNU / Linux 배포판에서는 다음 중 하나입니다.

#!/usr/local/bin/python 

또는

#!/usr/bin/python

그리고 모든 것 (OS / X) 중 최고의 상용 Unix sw / hw 시스템에서는 다음과 같습니다.

#!/Applications/MacPython 2.5/python

또는 FreeBSD에서 :

#!/usr/local/bin/python

그러나 이러한 모든 차이점은 다음을 사용하여 스크립트를 모두 이식 가능하게 만들 수 있습니다.

#!/usr/bin/env python

execLinux 커널 시스템 호출은 #!기본적으로 shebangs ( )를 이해합니다.

bash에서 할 때 :

./something

Linux에서는 exec경로를 사용 하여 시스템 호출을 호출합니다 ./something.

이 커널 라인은 https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_script.c#L25에 전달 된 파일에서 호출됩니다 exec.

if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!'))

파일의 맨 처음 바이트를 읽고 #!.

비교가 참이면 나머지 줄은 Linux 커널에 의해 구문 분석되어 exec경로 /usr/bin/env python및 현재 파일을 첫 번째 인수로 사용하여 다시 호출 합니다.

/usr/bin/env python /path/to/script.py

이것은 #주석 문자로 사용 되는 모든 스크립팅 언어에서 작동 합니다.

예, 다음을 사용하여 무한 루프를 만들 수 있습니다.

printf '#!/a\n' | sudo tee /a
sudo chmod +x /a
/a

Bash는 오류를 인식합니다.

-bash: /a: /a: bad interpreter: Too many levels of symbolic links

#! just happens to be human readable, but that is not required.

If the file started with different bytes, then the exec system call would use a different handler. The other most important built-in handler is for ELF executable files: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_elf.c#L1305 which checks for bytes 7f 45 4c 46 (which also happens to be human readable for .ELF). Let's confirm that by reading the 4 first bytes of /bin/ls, which is an ELF executable:

head -c 4 "$(which ls)" | hd 

output:

00000000  7f 45 4c 46                                       |.ELF|
00000004                                                                 

So when the kernel sees those bytes, it takes the ELF file, puts it into memory correctly, and starts a new process with it. See also: How does kernel get an executable binary file running under linux?

Finally, you can add your own shebang handlers with the binfmt_misc mechanism. For example, you can add a custom handler for .jar files. This mechanism even supports handlers by file extension. Another application is to transparently run executables of a different architecture with QEMU.

I don't think POSIX specifies shebangs however: https://unix.stackexchange.com/a/346214/32558 , although it does mention in on rationale sections, and in the form "if executable scripts are supported by the system something may happen". macOS and FreeBSD also seem to implement it however.

PATH search motivation

Likely, one big motivation for the existence of shebangs is the fact that in Linux, we often want to run commands from PATH just as:

basename-of-command

instead of:

/full/path/to/basename-of-command

But then, without the shebang mechanism, how would Linux know how to launch each type of file?

Hardcoding the extension in commands:

 basename-of-command.py

or implementing PATH search on every interpreter:

python basename-of-command

would be a possibility, but this has the major problem that everything breaks if we ever decide to refactor the command into another language.

Shebangs solve this problem beautifully.


It probably makes sense to emphasize one thing that the most have missed, which may prevent immediate understanding. When you type python in terminal you don't normally provide a full path. Instead, the executable is up looked in PATH environment variable. In turn, when you want to execute a Python program directly, /path/to/app.py, one must tell the shell what interpreter to use (via the hashbang, what the other contributors are explaining above).

Hashbang expects full path to an interpreter. Thus to run your Python program directly you have to provide full path to Python binary which varies significantly, especially considering a use of virtualenv. To address portability the trick with /usr/bin/env is used. The latter is originally intended to alter environment in-place and run a command in it. When no alteration is provided it runs the command in current environment, which effectively results in the same PATH lookup which does the trick.

Source from unix stackexchange


This is a shell convention that tells the shell which program can execute the script.

#!/usr/bin/env python

resolves to a path to the Python binary.


It's recommended way, proposed in documentation:

2.2.2. Executable Python Scripts

On BSD’ish Unix systems, Python scripts can be made directly executable, like shell scripts, by putting the line

#! /usr/bin/env python3.2

from http://docs.python.org/py3k/tutorial/interpreter.html#executable-python-scripts


You can try this issue using virtualenv

Here is test.py

#! /usr/bin/env python
import sys
print(sys.version)

Create virtual environments

virtualenv test2.6 -p /usr/bin/python2.6
virtualenv test2.7 -p /usr/bin/python2.7

activate each environment then check the differences

echo $PATH
./test.py

It just specifies what interpreter you want to use. To understand this, create a file through terminal by doing touch test.py, then type into that file the following:

#!/usr/bin/env python3
print "test"

and do chmod +x test.py to make your script executable. After this when you do ./test.py you should get an error saying:

  File "./test.py", line 2
    print "test"
               ^
SyntaxError: Missing parentheses in call to 'print'

because python3 doesn't supprt the print operator.

Now go ahead and change the first line of your code to:

#!/usr/bin/env python2

and it'll work, printing test to stdout, because python2 supports the print operator. So, now you've learned how to switch between script interpreters.


It seems to me like the files run the same without that line.

If so, then perhaps you're running the Python program on Windows? Windows doesn't use that line—instead, it uses the file-name extension to run the program associated with the file extension.

However in 2011, a "Python launcher" was developed which (to some degree) mimics this Linux behaviour for Windows. This is limited just to choosing which Python interpreter is run — e.g. to select between Python 2 and Python 3 on a system where both are installed. The launcher is optionally installed as py.exe by Python installation, and can be associated with .py files so that the launcher will check that line and in turn launch the specified Python interpreter version.


This is meant as more of historical information than a "real" answer.

Remember that back in the day you had LOTS of unix like operating systems whose designers all had their own notion of where to put stuff, and sometimes didn't include Python, Perl, Bash, or lots of other GNU/Open Source stuff at all.

This was even true of different Linux distributions. On Linux--pre-FHS[1]-you might have python in /usr/bin/ or /usr/local/bin/. Or it might not have been installed, so you built your own and put it in ~/bin

Solaris was the worst I ever worked on, partially as the transition from Berkeley Unix to System V. You could wind up with stuff in /usr/, /usr/local/, /usr/ucb, /opt/ etc. This could make for some really long paths. I have memories of the stuff from Sunfreeware.com installing each package in it's own directory, but I can't recall if it symlinked the binaries into /usr/bin or not.

Oh, and sometimes /usr/bin was on an NFS server[2].

So the env utility was developed to work around this.

Then you could write #!/bin/env interpreter and as long as the path was proper things had a reasonable chance of running. Of course, reasonable meant (for Python and Perl) that you had also set the appropriate environmental variables. For bash/ksh/zsh it just worked.

This was important because people were passing around shell scripts (like perl and python) and if you'd hard coded /usr/bin/python on your Red Hat Linux workstation it was going to break bad on a SGI...well, no, I think IRIX put python in the right spot. But on a Sparc station it might not run at all.

I miss my sparc station. But not a lot. Ok, now you've got me trolling around on E-Bay. Bastages.

[1] File-system Hierarchy Standard. https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard

[2] Yes, and sometimes people still do stuff like that. And no, I did not wear either a turnip OR an onion on my belt.


If you're running your script in a virtual environment, say venv, then executing which python while working on venv will display the path to the Python interpreter:

~/Envs/venv/bin/python

Note that the name of the virtual environment is embedded in the path to the Python interpreter. Therefore, hardcoding this path in your script will cause two problems:

  • If you upload the script to a repository, you're forcing other users to have the same virtual environment name. This is if they identify the problem first.
  • You won't be able to run the script across multiple virtual environments even if you had all required packages in other virtual environments.

Therefore, to add to Jonathan's answer, the ideal shebang is #!/usr/bin/env python, not just for portability across OSes but for portability across virtual environments as well!


Considering the portability issues between python2 and python3, you should always specify either version unless your program is compatible with both.

Some distributions are shipping python symlinked to python3 for a while now - do not rely on python being python2.

This is emphasized by PEP 394:

In order to tolerate differences across platforms, all new code that needs to invoke the Python interpreter should not specify python, but rather should specify either python2 or python3 (or the more specific python2.x and python3.x versions; see the Migration Notes). This distinction should be made in shebangs, when invoking from a shell script, when invoking via the system() call, or when invoking in any other context.


It tells the interpreter which version of python to run the program with when you have multiple versions of python.


It allows you to select the executable that you wish to use; which is very handy if perhaps you have multiple python installs, and different modules in each and wish to choose. e.g.

#!/bin/sh
#
# Choose the python we need. Explanation:
# a) '''\' translates to \ in shell, and starts a python multi-line string
# b) "" strings are treated as string concat by python, shell ignores them
# c) "true" command ignores its arguments
# c) exit before the ending ''' so the shell reads no further
# d) reset set docstrings to ignore the multiline comment code
#
"true" '''\'
PREFERRED_PYTHON=/Library/Frameworks/Python.framework/Versions/2.7/bin/python
ALTERNATIVE_PYTHON=/Library/Frameworks/Python.framework/Versions/3.6/bin/python3
FALLBACK_PYTHON=python3

if [ -x $PREFERRED_PYTHON ]; then
    echo Using preferred python $ALTERNATIVE_PYTHON
    exec $PREFERRED_PYTHON "$0" "$@"
elif [ -x $ALTERNATIVE_PYTHON ]; then
    echo Using alternative python $ALTERNATIVE_PYTHON
    exec $ALTERNATIVE_PYTHON "$0" "$@"
else
    echo Using fallback python $FALLBACK_PYTHON
    exec python3 "$0" "$@"
fi
exit 127
'''

__doc__ = """What this file does"""
print(__doc__)
import platform
print(platform.python_version())

this tells the script where is python directory !

#! /usr/bin/env python

참고URL : https://stackoverflow.com/questions/2429511/why-do-people-write-the-usr-bin-env-python-shebang-on-the-first-line-of-a-pyt

반응형