¿Por qué la gente escribe el #! / Usr / bin / env python shebang en la primera línea de un script Python?


1047

Me parece que los archivos se ejecutan igual sin esa línea.


1
La respuesta a continuación que dice que es solo una línea de comentarios. Ese no es siempre el caso. Tengo un "¡Hola, mundo!" Script CGI (.py) que solo se ejecutará y mostrará la página web #!/usr/bin/env pythonen la parte superior.
Chakotay


Pueden correr, pero no en el entorno previsto
Nicholas Hamilton

18
He visitado esta publicación muchas veces en 7 años porque a veces olvido el hashbang env. Copiar pasta :)
BugHunterUK

Respuestas:


1084

Si tiene varias versiones de Python instaladas, /usr/bin/envse asegurará de que el intérprete utilizado sea el primero en el entorno.$PATH . La alternativa sería codificar algo así #!/usr/bin/python; está bien, pero menos flexible.

En Unix, un archivo ejecutable que debe interpretarse puede indicar qué intérprete usar al tener un#! al comienzo de la primera línea, seguido del intérprete (y cualquier indicador que pueda necesitar).

Si habla de otras plataformas, por supuesto, esta regla no se aplica (pero esa "línea shebang" no hace daño y ayudará si alguna vez copia ese script en una plataforma con una base Unix, como Linux, Mac , etc.)


267
Solo para agregar: esto se aplica cuando lo ejecuta en Unix haciéndolo ejecutable ( chmod +x myscript.py) y luego ejecutándolo directamente: en ./myscript.pylugar de solo python myscript.py.
Craig McQueen

28
el uso envproporciona la máxima flexibilidad, ya que el usuario puede seleccionar el intérprete para usar cambiando la RUTA. Sin embargo, a menudo esta flexibilidad no es necesaria y la desventaja es que Linux, por ejemplo, no puede usar el nombre del script para el nombre del proceso psy vuelve a "python". Al empaquetar aplicaciones de Python para distribuciones, por ejemplo, recomendaría no usar env.
pixelbeat

99
pyEl lanzador puede usar la línea shebang en Windows. Se incluye en Python 3.3 o se puede instalar de forma independiente .
jfs

66
Una palabra importante de advertencia, el valor de retorno de env finalmente expira. Es poco probable que lo afecte si está ejecutando procesos de corta duración. Sin embargo, he tenido procesos que mueren con el mensaje /usr/bin/env: Key has expireddespués de muchas horas.
malaverdiere

44
@malaverdiere, ¿puede vincular a algún recurso que explique este comportamiento de vencimiento? No los puedo encontrar.
Michael

267

Eso se llama la línea shebang . Como explica la entrada de Wikipedia :

En informática, un shebang (también llamado hashbang, hashpling, pound bang o crunchbang) se refiere a los caracteres "#!" cuando son los dos primeros caracteres en una directiva de intérprete como la primera línea de un archivo de texto. En un sistema operativo tipo Unix, el cargador de programas toma la presencia de estos dos caracteres como una indicación de que el archivo es un script e intenta ejecutar ese script utilizando el intérprete especificado por el resto de la primera línea del archivo.

Consulte también la entrada de Preguntas frecuentes de Unix .

Incluso en Windows, donde la línea shebang no determina el intérprete que se ejecutará, puede pasar opciones al intérprete al especificarlas en la línea shebang. Me resulta útil mantener una línea shebang genérica en scripts únicos (como los que escribo al responder preguntas en SO), para poder probarlos rápidamente en Windows y ArchLinux .

La utilidad env le permite invocar un comando en la ruta:

El primer argumento restante especifica el nombre del programa a invocar; se busca según la PATHvariable de entorno. Cualquier argumento restante se pasa como argumento a ese programa.


30
Fácil de encontrar con Google, si se conocen las palabras clave (la "línea shebang" es esencial).
Arafangion

14
En realidad, esta explicación es más clara que otras referencias que verifiqué con Google. Siempre es mejor obtener una explicación de 1 párrafo dirigida a la pregunta, en lugar de leer un manual completo que aborde cada uso potencial.
Sam Goldberg

1
@Arafangion probablemente encuentre útil esta pregunta . TL; DR: symbolhound.com
ulidtko

@ulidtko: Interesante motor de búsqueda, considere escribir una respuesta para que la pregunta de John García tenga una mejor respuesta.
Arafangion

1
"Incluso en Windows, donde la línea shebang no determina el intérprete que se debe ejecutar, puede pasar opciones al intérprete al especificarlas en la línea shebang". Eso es simplemente falso; Si sucede algo así, es porque el propio intérprete está procesando la línea shebang. Si el intérprete no tiene un reconocimiento especial para las líneas shebang, entonces no sucede tal cosa. Windows no hace nada con líneas shebang ". Lo que puede estar describiendo en este caso es el lanzador de python: python.org/dev/peps/pep-0397 .
Kaz

154

Ampliando un poco las otras respuestas, aquí hay un pequeño ejemplo de cómo sus scripts de línea de comando pueden meterse en problemas por el uso incauto de las /usr/bin/envlíneas shebang:

$ /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

El módulo json no existe en Python 2.5.

Una forma de protegerse contra ese tipo de problema es usar los nombres de comandos de Python versionados que normalmente se instalan con la mayoría de las Pythons:

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

Si solo necesita distinguir entre Python 2.xy Python 3.x, las versiones recientes de Python 3 también proporcionan un python3nombre:

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

27
Hmm, eso no es lo que obtuve de esa publicación.
Glenn Jackman

1
Diferencia entre local y global. Si which pythonvuelve /usr/bin/python, una ruta de directorio local podría estar codificado: #!/usr/bin/python. Pero eso es menos flexible que el #!/usr/bin/env pythonque tiene una aplicación global.
noobninja

85

Para ejecutar el script de Python, necesitamos decirle al shell tres cosas:

  1. Que el archivo es un script
  2. A qué intérprete queremos ejecutar el script
  3. El camino de dicho intérprete

El shebang #!cumple (1.). El shebang comienza con un #porque el #personaje es un marcador de comentario en muchos lenguajes de secuencias de comandos. Por lo tanto, el intérprete ignora automáticamente el contenido de la línea shebang.

El envcomando cumple (2.) y (3.). Para citar "gravedad",

Un uso común del envcomando es lanzar intérpretes, haciendo uso del hecho de que env buscará en $ PATH el comando que se le indica que inicie. Dado que la línea shebang requiere que se especifique una ruta absoluta y que la ubicación de varios intérpretes (perl, bash, python) puede variar mucho, es común usar:

#!/usr/bin/env perl  en lugar de tratar de adivinar si es / bin / perl, / usr / bin / perl, / usr / local / bin / perl, / usr / local / pkg / perl, / fileserver / usr / bin / perl, o / home / MrDaniel / usr / bin / perl en el sistema del usuario ...

Por otro lado, env casi siempre está en / usr / bin / env. (Excepto en los casos en que no lo es; algunos sistemas pueden usar / bin / env, pero esa es una ocasión bastante rara y solo ocurre en sistemas que no son Linux).


1
"Grawity" donde?
Pacerier

44

Quizás su pregunta es en este sentido:

Si quieres usar: $python myscript.py

No necesitas esa línea en absoluto. El sistema llamará a python y luego el intérprete de python ejecutará su script.

Pero si tiene la intención de usar: $./myscript.py

Al llamarlo directamente como un programa normal o un script bash, debe escribir esa línea para especificar al sistema qué programa usar para ejecutarlo (y también hacer que sea ejecutable con chmod 755)


o puedes escribir python3 myscript.py
vijay shanker

44

La execllamada al sistema del kernel de Linux comprende shebangs ( #!) de forma nativa

Cuando lo haces en bash:

./something

en Linux, esto llama a la execllamada del sistema con la ruta ./something.

Esta línea del núcleo se llama en el archivo pasado a exec: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_script.c#L25

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

Lee los primeros bytes del archivo y los compara con #!.

Si la comparación es verdadera, el núcleo de Linux analiza el resto de la línea, que realiza otra execllamada con la ruta /usr/bin/env pythony el archivo actual como primer argumento:

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

y esto funciona para cualquier lenguaje de secuencias de comandos que se use #como carácter de comentario.

Y sí, puedes hacer un ciclo infinito con:

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

Bash reconoce el error:

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

#! Simplemente resulta ser legible para los humanos, pero eso no es obligatorio.

Si el archivo comenzó con diferentes bytes, la execllamada al sistema usaría un controlador diferente. El otro controlador incorporado más importante es para archivos ejecutables ELF: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_elf.c#L1305 que comprueba los bytes 7f 45 4c 46(que también son humanos legible para .ELF). Confirmemos eso leyendo los 4 primeros bytes de /bin/ls, que es un ejecutable ELF:

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

salida:

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

Entonces, cuando el kernel ve esos bytes, toma el archivo ELF, lo guarda en la memoria correctamente y comienza un nuevo proceso con él. Ver también: ¿Cómo obtiene el núcleo un archivo binario ejecutable que se ejecuta en Linux?

Finalmente, puede agregar sus propios manejadores de shebang con el binfmt_miscmecanismo. Por ejemplo, puede agregar un controlador personalizado para .jararchivos . Este mecanismo incluso admite controladores por extensión de archivo. Otra aplicación es ejecutar de forma transparente ejecutables de una arquitectura diferente con QEMU .

Sin embargo , no creo que POSIX especifique shebangs: https://unix.stackexchange.com/a/346214/32558 , aunque sí menciona en las secciones de justificación, y en la forma "si el sistema admite scripts ejecutables, algo puede suceder". macOS y FreeBSD también parecen implementarlo sin embargo.

PATH buscar motivación

Probablemente, una gran motivación para la existencia de shebangs es el hecho de que en Linux, a menudo queremos ejecutar comandos desde PATH:

basename-of-command

en vez de:

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

Pero entonces, sin el mecanismo shebang, ¿cómo sabría Linux cómo iniciar cada tipo de archivo?

Codificar la extensión en comandos:

 basename-of-command.py

o implementar la búsqueda PATH en cada intérprete:

python basename-of-command

sería una posibilidad, pero este tiene el principal problema de que todo se rompe si alguna vez decidimos refactorizar el comando a otro idioma.

Los shebangs resuelven este problema maravillosamente.


39

Técnicamente, en Python, esto es solo una línea de comentarios.

Esta línea solo se usa si ejecuta el script py desde el shell (desde la línea de comando). Esto se conoce como el " Shebang !" , y se usa en varias situaciones, no solo con scripts de Python.

Aquí, le indica al shell que inicie una versión específica de Python (para encargarse del resto del archivo.


El shebang es un concepto de Unix. Vale la pena mencionar que también funciona en Windows si ha instalado el iniciador de Python py.exe . Esto es parte de una instalación estándar de Python.
florisla

38

La razón principal para hacer esto es hacer que el script sea portátil en todos los entornos del sistema operativo.

Por ejemplo, bajo mingw, los scripts de python usan:

#!/c/python3k/python 

y bajo distribución GNU / Linux es:

#!/usr/local/bin/python 

o

#!/usr/bin/python

y bajo el mejor sistema comercial Unix sw / hw de todos (OS / X), es:

#!/Applications/MacPython 2.5/python

o en FreeBSD:

#!/usr/local/bin/python

Sin embargo, todas estas diferencias pueden hacer que el script sea portátil a través de todos al usar:

#!/usr/bin/env python

2
Bajo MacOSX, también lo es /usr/bin/python. Bajo Linux, el Python instalado por el sistema también es casi seguro /usr/bin/python(nunca he visto nada más y no tendría sentido). Tenga en cuenta que puede haber sistemas que no tienen /usr/bin/env.
Albert

1
Si está en OSX y usa Homebrew y sigue sus instrucciones de instalación predeterminadas, estará en #! / Usr / local / bin / python
Será

@ Jean-PaulCalderone: Vea la respuesta de saaj a continuación.
pydsigner

Actualización para el año 2018: Bare pythonno es tan portátil, es el intérprete predeterminado de distribución de Python. Arch Linux por defecto es Python 3 durante mucho tiempo y es posible que las distribuciones también lo piensen porque Python 2 solo es compatible hasta 2020.
mati865

22

Probablemente tenga sentido enfatizar una cosa que la mayoría se ha perdido, lo que puede impedir la comprensión inmediata. Cuando escribe pythonen la terminal, normalmente no proporciona una ruta completa. En cambio, el ejecutable se busca en PATHla variable de entorno. A su vez, cuando desea ejecutar un programa Python directamente, /path/to/app.pyuno debe decirle al intérprete qué intérprete usar (a través del hashbang , lo que los otros contribuyentes están explicando anteriormente).

Hashbang espera el camino completo a un intérprete. Por lo tanto, para ejecutar su programa Python directamente, debe proporcionar la ruta completa al binario de Python que varía significativamente, especialmente considerando el uso de virtualenv . Para abordar la portabilidad /usr/bin/envse utiliza el truco con . Este último está destinado originalmente a alterar el entorno en el lugar y ejecutar un comando en él. Cuando no se proporciona ninguna alteración, ejecuta el comando en el entorno actual, lo que efectivamente resulta en la misma PATHbúsqueda que hace el truco.

Fuente de unix stackexchange


14

Esta es una convención de shell que le dice al shell qué programa puede ejecutar el script.

#! / usr / bin / env python

resuelve una ruta al binario de Python.



9

Puedes probar este problema usando virtualenv

Aquí está test.py

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

Crea ambientes virtuales

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

active cada entorno y luego verifique las diferencias

echo $PATH
./test.py

9

Solo especifica qué intérprete desea utilizar. Para comprender esto, cree un archivo a través de la terminal haciendo touch test.py, luego escriba en ese archivo lo siguiente:

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

y haz chmod +x test.pyque tu script sea ejecutable. Después de esto, cuando lo haga ./test.py, debería recibir un error que diga:

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

porque python3 no admite el operador de impresión.

Ahora continúe y cambie la primera línea de su código a:

#!/usr/bin/env python2

y funcionará, imprimiendo testen stdout, porque python2 es compatible con el operador de impresión. Entonces, ahora has aprendido a cambiar entre intérpretes de guiones.


9

Me parece que los archivos se ejecutan igual sin esa línea.

Si es así, ¿tal vez estás ejecutando el programa Python en Windows? Windows no usa esa línea; en su lugar, usa la extensión de nombre de archivo para ejecutar el programa asociado con la extensión de archivo.

Sin embargo, en 2011, se desarrolló un "iniciador de Python" que (hasta cierto punto) imita este comportamiento de Linux para Windows. Esto se limita solo a elegir qué intérprete de Python se ejecuta, por ejemplo, para seleccionar entre Python 2 y Python 3 en un sistema donde ambos están instalados. El iniciador se instala opcionalmente como py.exemediante la instalación de Python, y puede asociarse con .pyarchivos para que el iniciador verifique esa línea y, a su vez, inicie la versión del intérprete de Python especificada.


66
Él también podría estar usando $ python myscript.py.
Sinan Ünür

Cometí el error al no tener la línea y usé python script.py, y un día simplemente hice ./myscript.py y todo dejó de funcionar, luego me di cuenta de que el sistema está buscando el archivo como un script de shell en lugar de un script de python.
Guagua

8

Esto significa más información histórica que una respuesta "real".

Recuerda que en su día que tenían un montón de sistemas operativos tipo UNIX cuyos diseñadores todos tenían su propia idea de dónde colocar las cosas, ya veces no incluía Python, Perl, Bash, o un montón de otros GNU / Open Source cosas en absoluto .

Esto fue incluso cierto para diferentes distribuciones de Linux. En Linux - pre-FHS [1] -puede tener python en / usr / bin / o / usr / local / bin /. O puede que no se haya instalado, por lo que creó el suyo propio y lo colocó en ~ / bin

Solaris fue el peor en el que he trabajado, en parte como la transición de Berkeley Unix al Sistema V. Podría terminar con cosas en / usr /, / usr / local /, / usr / ucb, / opt / etc. Esto podría hacer que por algunos caminos realmente largos. Tengo recuerdos de las cosas de Sunfreeware.com instalando cada paquete en su propio directorio, pero no recuerdo si unía los enlaces binarios a / usr / bin o no.

Ah, y a veces / usr / bin estaba en un servidor NFS [2].

Entonces, la envutilidad fue desarrollada para solucionar esto.

Entonces podías escribir #!/bin/env interpretery mientras el camino fuera correcto, las cosas tenían una posibilidad razonable de correr. Por supuesto, razonable significaba (para Python y Perl) que también había establecido las variables ambientales apropiadas. Para bash / ksh / zsh simplemente funcionó.

Esto era importante porque la gente pasaba por scripts de shell (como perl y python) y si codificabas / usr / bin / python en tu estación de trabajo Red Hat Linux, se rompería mal en un SGI ... bueno, no , Creo que IRIX puso a Python en el lugar correcto. Pero en una estación Sparc podría no funcionar en absoluto.

Echo de menos mi estación de sparc. Pero no mucho. Ok, ahora me tienes dando vueltas en E-Bay. Bastajes

[1] Estándar de jerarquía del sistema de archivos. https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard

[2] Sí, y a veces la gente todavía hace cosas así. Y no, no llevaba ni un nabo ni una cebolla en el cinturón.


5

Si está ejecutando su script en un entorno virtual, por ejemplo venv, al ejecutar which pythonmientras trabaja, venvse mostrará la ruta al intérprete de Python:

~/Envs/venv/bin/python

Tenga en cuenta que el nombre del entorno virtual está incrustado en la ruta al intérprete de Python. Por lo tanto, codificar esta ruta en su script causará dos problemas:

  • Si carga el script en un repositorio, está obligando a otros usuarios a tener el mismo nombre de entorno virtual . Esto es si primero identifican el problema.
  • No podrá ejecutar el script en varios entornos virtuales, incluso si tuviera todos los paquetes necesarios en otros entornos virtuales.

Por lo tanto, para agregar a la respuesta de Jonathan , ¡el shebang ideal es #!/usr/bin/env python, no solo para la portabilidad entre sistemas operativos, sino también para la portabilidad en entornos virtuales!


3

Teniendo en cuenta los problemas de portabilidad entre python2y python3, siempre debe especificar cualquiera de las versiones a menos que su programa sea compatible con ambas.

Algunas distribuciones se envían por pythonenlaces simbólicos python3desde hace un tiempo, no confíes en pythonser python2.

PEP 394 enfatiza esto :

Para tolerar diferencias entre plataformas, todo el código nuevo que necesita invocar al intérprete de Python no debe especificar python, sino que debe especificar python2 o python3 (o las versiones más específicas de python2.xy python3.x; consulte las Notas de migración ) . Esta distinción debe hacerse en shebangs, cuando se invoca desde un script de shell, cuando se invoca a través de la llamada system (), o cuando se invoca en cualquier otro contexto.


2

Le dice al intérprete con qué versión de python ejecutar el programa cuando tiene varias versiones de python.


0

Le permite seleccionar el ejecutable que desea usar; lo cual es muy útil si quizás tiene varias instalaciones de Python y diferentes módulos en cada una y desea elegir. p.ej

#!/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())

-8

¡Esto le dice al script dónde está el directorio de Python!

#! /usr/bin/env python
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.