Noté que algunos scripts que he adquirido de otros tienen el shebang #!/path/to/NAME
mientras que otros (usando la misma herramienta, NAME) tienen el shebang #!/usr/bin/env NAME
.
Ambos parecen funcionar correctamente. En los tutoriales (en Python, por ejemplo), parece haber una sugerencia de que este último shebang es mejor. Pero, no entiendo por qué esto es así.
Me doy cuenta de que, para usar el último shebang, NAME debe estar en la RUTA, mientras que el primer shebang no tiene esta restricción.
Además, parece (para mí) que el primero sería el mejor shebang, ya que especifica con precisión dónde se encuentra NAME. Entonces, en este caso, si hay varias versiones de NAME (por ejemplo, / usr / bin / NAME, / usr / local / bin / NAME), el primer caso especifica cuál usar.
Mi pregunta es ¿por qué se prefiere el primer shebang al segundo?
fuente
Respuestas:
No es necesariamente mejor.
La ventaja de esto
#!/usr/bin/env python
es que usará cualquierpython
ejecutable que aparezca primero en el usuario$PATH
.La desventaja de
#!/usr/bin/env python
es que va a utilizar cualquierpython
ejecutable aparece por primera vez en el usuario de$PATH
.Eso significa que el script podría comportarse de manera diferente dependiendo de quién lo ejecute. Para un usuario, podría usar el
/usr/bin/python
que se instaló con el sistema operativo. Por otro lado, podría usar un experimental/home/phred/bin/python
que no funciona correctamente.Y si
python
sólo se instala en/usr/local/bin
, un usuario que no tiene/usr/local/bin
en$PATH
ni siquiera será capaz de ejecutar el script. (Eso probablemente no sea muy probable en los sistemas modernos, pero podría suceder fácilmente para un intérprete más oscuro).Al especificar
#!/usr/bin/python
, especifica exactamente qué intérprete se utilizará para ejecutar el script en un sistema en particular .Otro problema potencial es que el
#!/usr/bin/env
truco no le permite pasar argumentos al intérprete (aparte del nombre del script, que se pasa implícitamente). Esto generalmente no es un problema, pero puede serlo. Muchos scripts de Perl están escritos con#!/usr/bin/perl -w
, perouse warnings;
es el reemplazo recomendado en estos días. Los scripts Csh deberían usar#!/bin/csh -f
, pero los scripts csh no se recomiendan en primer lugar. Pero podría haber otros ejemplos.Tengo varias secuencias de comandos Perl en un sistema de control de fuente personal que instalo cuando configuro una cuenta en un nuevo sistema. Utilizo un script de instalador que modifica la
#!
línea de cada script a medida que lo instala en mi$HOME/bin
. (No he tenido que usar nada más que#!/usr/bin/perl
últimamente; se remonta a los tiempos en que Perl a menudo no se instalaba de manera predeterminada).Un punto menor: el
#!/usr/bin/env
truco es posiblemente un abuso delenv
comando, que originalmente tenía la intención (como su nombre lo indica) de invocar un comando con un entorno alterado. Además, algunos sistemas más antiguos (incluido SunOS 4, si no recuerdo mal) no tenían elenv
comando/usr/bin
. Es probable que ninguno de estos sea una preocupación importante.env
funciona de esta manera, muchos scripts utilizan el#!/usr/bin/env
truco, y es probable que los proveedores de sistemas operativos no hagan nada para romperlo. Que podría ser un problema si usted quiere que su secuencia de comandos para ejecutar en un sistema muy viejo, pero entonces es muy probable que tenga que modificar todos modos.Otro posible problema, (gracias a Sopalajo de Arrierez por señalarlo en los comentarios) es que los trabajos cron se ejecutan con un entorno restringido. En particular,
$PATH
es típicamente algo así/usr/bin:/bin
. Entonces, si el directorio que contiene el intérprete no se encuentra en uno de esos directorios, incluso si está en su$PATH
shell predeterminado de usuario, entonces el/usr/bin/env
truco no funcionará. Puede especificar la ruta exacta, o puede agregar una línea a su crontab para establecer$PATH
(man 5 crontab
para más detalles).fuente
use v5.12;
sirve para algo de ese propósito. Y#!/usr/bin/env perl5.12
fallará si el sistema tiene Perl 5.14 pero no 5.12. Para Python 2 vs. 3,#!/usr/bin/python2
y#!/usr/bin/python3
es probable que funcionen./usr/local/bin
. Sé que funciona correctamente con/usr/bin/perl
. No tengo idea de si funciona con cualquierperl
ejecutable que algún usuario aleatorio tenga en su cuenta$PATH
. Quizás alguien esté experimentando con alguna versión antigua de Perl; porque especifiqué#!/usr/bin/perl
, mi secuencia de comandos (que el usuario no necesariamente sabe ni le importa que sea una secuencia de comandos Perl) no dejará de funcionar./usr/bin/perl
, lo descubriré muy rápidamente, y es responsabilidad del propietario / administrador del sistema mantenerlo actualizado. Si desea ejecutar mi script con su propio perl, siéntase libre de tomar y modificar una copia o invocarlo a través deperl foo
. (Y podría considerar la posibilidad de que las 55 personas que votaron por esta respuesta también sepan una o dos cosas. Ciertamente es posible que tenga razón y todos estén equivocados, pero esa no es la forma en que apostaría.)Porque / usr / bin / env puede interpretar tu
$PATH
, lo que hace que los scripts sean más portátiles.Solo ejecutará su script si python está instalado en / usr / local / bin.
Interpretará su
$PATH
, y encontrará python en cualquier directorio en su$PATH
.Por lo tanto, su script es más portátil y funcionará sin modificaciones en los sistemas donde python está instalado como
/usr/bin/python
, o/usr/local/bin/python
, o incluso en directorios personalizados (que se han agregado a$PATH
), como/opt/local/bin/python
.La portabilidad es la única razón por la que
env
se prefiere el uso a las rutas codificadas.fuente
python
ejecutables son particularmente comunes a medida quevirtualenv
aumenta el uso.#! python
, por qué no se usa eso?#! python
no se usa porque tendría que estar en el mismo directorio que el binario de Python, ya que la palabra básicapython
se interpreta como la ruta completa al archivo. Si no tiene un binario de Python en el directorio actual, obtendrá un error comobash: ./script.py: python: bad interpreter: No such file or directory
. Es lo mismo que si usaras#! /not/a/real/path/python
Especificar la ruta absoluta es más precisa en un sistema dado. La desventaja es que es demasiado preciso. Suponga que se da cuenta de que la instalación del sistema de Perl es demasiado antigua para sus scripts y desea utilizar el suyo en su lugar: luego tiene que editar los scripts y cambiar
#!/usr/bin/perl
a#!/home/myname/bin/perl
. Peor aún, si tiene Perl en/usr/bin
algunas máquinas,/usr/local/bin
en otras y/home/myname/bin/perl
en otras máquinas, deberá mantener tres copias separadas de los scripts y ejecutar la correspondiente en cada máquina.#!/usr/bin/env
se rompe siPATH
es malo, pero también lo hace casi todo. Intentar operar con un malPATH
rara vez es útil e indica que sabe muy poco sobre el sistema en el que se ejecuta el script, por lo que no puede confiar en ninguna ruta absoluta de todos modos.Hay dos programas cuya ubicación puede confiar en casi todas las variantes de Unix:
/bin/sh
y/usr/bin/env
. Algunas variantes de Unix oscuras y en su mayoría retiradas tenían/bin/env
sin tener/usr/bin/env
, pero es poco probable que las encuentres. Los sistemas modernos tienen/usr/bin/env
precisamente debido a su uso generalizado en shebangs./usr/bin/env
es algo con lo que puedes contar.Además
/bin/sh
, la única vez que debe usar una ruta absoluta en un shebang es cuando su script no debe ser portátil, por lo que puede contar con una ubicación conocida para el intérprete. Por ejemplo, un script bash que solo funciona en Linux puede usarse de manera segura#!/bin/bash
. Un script que solo debe usarse internamente puede basarse en las convenciones de ubicación del intérprete interno.#!/usr/bin/env
Tiene inconvenientes. Es más flexible que especificar una ruta absoluta, pero aún requiere conocer el nombre del intérprete. Ocasionalmente, es posible que desee ejecutar un intérprete que no esté en$PATH
, por ejemplo, en una ubicación relativa al script. En tales casos, a menudo puede crear un script políglota que puede ser interpretado tanto por el shell estándar como por el intérprete deseado. Por ejemplo, para hacer que un script de Python 2 sea portátil tanto en sistemas dondepython
está Python 3 ypython2
Python 2 como en sistemas dondepython
está Python 2 ypython2
no existe:fuente
Específicamente para Perl, usar
#!/usr/bin/env
es una mala idea por dos razones.Primero, no es portátil. En algunas plataformas oscuras, env no está en / usr / bin. En segundo lugar, como ha señalado Keith Thompson , puede causar problemas para pasar argumentos en la línea shebang. La solución máximamente portátil es esta:
Para obtener detalles sobre cómo funciona, consulte 'perldoc perlrun' y lo que dice sobre el argumento -x.
fuente
La razón por la que hay una distinción entre los dos es por cómo se ejecutan los scripts.
Se requiere el uso
/usr/bin/env
(que, como se mencionó en otras respuestas, no está en/usr/bin
algunos sistemas operativos) porque no puede simplemente poner un nombre ejecutable después de#!
- debe ser una ruta absoluta. Esto se debe a que el#!
mecanismo funciona en un nivel más bajo que el shell. Es parte del cargador binario del núcleo. Esto puede ser probado. Ponga esto en un archivo y márquelo como ejecutable:Encontrará que imprime un error como este cuando intenta ejecutarlo:
Si un archivo está marcado como ejecutable y comienza con a
#!
, el núcleo (que no conoce$PATH
o el directorio actual: estos son conceptos de usuario) buscará un archivo utilizando una ruta absoluta. Debido a que usar una ruta absoluta es problemático (como se menciona en otras respuestas), a alguien se le ocurrió un truco: puede ejecutar/usr/bin/env
(que casi siempre está en esa ubicación) para ejecutar algo usando$PATH
.fuente
Hay dos problemas más con el uso
#!/usr/bin/env
No resuelve el problema de especificar la ruta completa al intérprete, simplemente lo mueve
env
.env
no se garantiza más que esté dentro de lo/usr/bin/env
quebash
se garantiza que esté dentro/bin/bash
o python in/usr/bin/python
.env
sobrescribe ARGV [0] con el nombre del intérprete (por ejemplo, bash o python).Esto evita que aparezca el nombre de su script, por ejemplo, en la
ps
salida (o cambia cómo / dónde aparece) y hace que sea imposible encontrarlo, por ejemplo,ps -C scriptname.sh
[actualización 2016-06-04]
Y un tercer problema:
Cambiar su RUTA es más trabajo que simplemente editar la primera línea de un guión, especialmente cuando el guión de tales ediciones es trivial. p.ej:
printf "%s\n" 1 i '#!'$(type -P python2) . w | ed foo.py
Anexar o pre-pendiente un directorio a $ PATH es bastante fácil (aunque todavía tiene que editar un archivo para que sea permanente, su
~/.profile
o lo que sea) y está lejos de ser fácil de escribir ESE edición porque la RUTA podría establecerse en cualquier parte del script , no en la primera línea).Cambiar el orden de los directorios PATH es significativamente más difícil ... y mucho más difícil que simplemente editar la
#!
línea.Y todavía tiene todos los otros problemas que
#!/usr/bin/env
le da el uso .@jlliagre sugiere en un comentario que
#!/usr/bin/env
es útil para probar su secuencia de comandos con múltiples versiones de intérpretes, "solo cambiando su orden PATH / PATH"Si necesita hacer eso, es mucho más fácil tener varias
#!
líneas en la parte superior de su script (son solo comentarios en cualquier lugar menos la primera línea) y cortar / copiar y pegar la que desea usar ahora para La primera línea.En
vi
, eso sería tan simple como mover el cursor al#!
línea que desea, luego escribirdd1GP
oY1GP
. Incluso con un editor tan trivial comonano
, usaría el mouse para copiar y pegar tomaría segundos.En general, las ventajas de usar
#!/usr/bin/env
son mínimas en el mejor de los casos, y ciertamente ni siquiera se acercan a superar las desventajas. Incluso la ventaja de "conveniencia" es en gran medida ilusoria.En mi opinión, es una idea tonta promovida por un tipo particular de programador que cree que los sistemas operativos no son algo con lo que se pueda trabajar, son un problema que se debe solucionar (o ignorar en el mejor de los casos).
PD: aquí hay un script simple para cambiar el intérprete de varios archivos a la vez.
change-shebang.sh
:Ejecútelo como, por ejemplo,
change-shebang.sh python2.7 *.py
ochange-shebang.sh $HOME/bin/my-experimental-ruby *.rb
fuente
csh
creación de secuencias de comandos es promover una mala solución: funciona, pero hay alternativas mucho mejores.#!
línea./usr/bin/env
y necesito deshacerme de él localmente, o si no lo usaron y necesito agregarlo. Ambos casos pueden ocurrir y, por lo tanto, los scripts proporcionados en esta respuesta son una herramienta potencialmente útil para tener en la caja de herramientas.Añadiendo otro ejemplo aquí:
El uso
env
también es útil cuando desea compartir scripts entre múltiplesrvm
entornos, por ejemplo.Al ejecutar esto en la línea cmd, se muestra qué versión de ruby se usará cuando
#!/usr/bin/env ruby
se use dentro de un script:env ruby --version
Por lo tanto, cuando usa
env
, puede usar diferentes versiones de ruby a través de rvm, sin cambiar sus scripts.fuente
Si está escribiendo exclusivamente para usted o para su trabajo y la ubicación del intérprete al que llama siempre está en el mismo lugar, utilice la ruta directa. En todos los demás casos, use
#!/usr/bin/env
.He aquí por qué: en su situación, el
python
intérprete estaba en el mismo lugar, independientemente de la sintaxis que utilizó, pero para muchas personas podría haberse instalado en un lugar diferente. Aunque la mayoría de los principales intérpretes de programación se encuentran en/usr/bin/
una gran cantidad de software más nuevo, está predeterminado/usr/local/bin/
.Incluso argumentaría que siempre se
#!/usr/bin/env
debe usar porque si tiene instaladas varias versiones del mismo intérprete y no sabe cuál será el valor predeterminado de su shell, entonces probablemente debería solucionarlo.fuente
Por razones de portabilidad y compatibilidad, es mejor usar
en lugar de
Hay múltiples posibilidades, donde un binario puede estar ubicado en un sistema Linux / Unix. Consulte la página de manual de hier (7) para obtener una descripción detallada de la jerarquía del sistema de archivos.
FreeBSD, por ejemplo, instala todo el software, que no es parte del sistema base, en / usr / local / . Dado que bash no es parte del sistema base, el binario bash se instala en / usr / local / bin / bash .
Cuando desee un script portátil bash / csh / perl / whatever, que se ejecuta en la mayoría de las distribuciones de Linux y FreeBSD, debe usar #! / Usr / bin / env .
También tenga en cuenta que la mayoría de las instalaciones de Linux también han vinculado (duro) el binario env a / bin / env o softlinked / usr / bin a / bin que no debe usarse en el shebang. Así que no uses #! / Bin / env .
fuente