En el encabezado de un script Bash, ¿cuál es la diferencia entre esas dos declaraciones:
#!/usr/bin/env bash
#!/usr/bin/bash
Cuando consulté la env
página de manual , obtuve esta definición:
env - run a program in a modified environment
Qué significa eso?
Respuestas:
Ejecutar un comando
/usr/bin/env
tiene la ventaja de buscar cualquiera que sea la versión predeterminada del programa en su entorno actual .De esta manera, no tiene que buscarlo en un lugar específico del sistema, ya que esas rutas pueden estar en diferentes ubicaciones en diferentes sistemas. Mientras esté en tu camino, lo encontrará.
Una desventaja es que no podrá pasar más de un argumento (por ejemplo, no podrá escribir
/usr/bin/env awk -f
) si desea admitir Linux, ya que POSIX es impreciso sobre cómo se interpretará la línea, y Linux interpreta todo después del primer espacio para denotar un solo argumento. Puede usar/usr/bin/env -S
algunas versiones deenv
para evitar esto, pero luego el script se volverá aún menos portátil y se romperá en sistemas bastante recientes (por ejemplo, incluso Ubuntu 16.04 si no más tarde).Otro inconveniente es que, dado que no está llamando a un ejecutable explícito, tiene el potencial de errores y problemas de seguridad de sistemas multiusuario (si alguien logró que su ejecutable sea llamado
bash
en su ruta, por ejemplo).En algunas situaciones, puede preferirse el primero (como ejecutar scripts de python con múltiples versiones de python, sin tener que volver a trabajar la línea ejecutable). Pero en situaciones donde la seguridad es el foco, se preferiría lo último, ya que limita las posibilidades de inyección de código.
fuente
#!/usr/bin/env echo Hello
se queja:/usr/bin/env: echo Hello: No such file or directory
. Aparentemente se trataecho Hello
como un argumento único para/usr/bin/env
.env
comando ciertamente permite pasar argumentos al comando. El problema es la semántica de la#!
línea, y eso depende del núcleo. Los núcleos de Linux recientes permiten cosas como#!/usr/bin/env command args
, pero los núcleos de Linux más antiguos y otros sistemas no.El uso
#!/usr/bin/env NAME
hace que el shell busque la primera coincidencia de NAME en la variable de entorno $ PATH. Puede ser útil si no conoce la ruta absoluta o no desea buscarla.fuente
En lugar de definir explícitamente la ruta al intérprete como en
/usr/bin/bash/
, mediante el comando env, se busca el intérprete y se inicia desde donde se encuentre por primera vez. Esto tiene ventajas y desventajasfuente
Si los scripts de shell comienzan con
#!/bin/bash
, siempre se ejecutarán conbash
from/bin
. Sin embargo, si comienzan con#!/usr/bin/env bash
, buscaránbash
adentro$PATH
y luego comenzarán con el primero que puedan encontrar.¿Por qué sería útil esto? Suponga que desea ejecutar
bash
scripts, que requieren bash 4.xo posterior, pero su sistema solo tienebash
3.x instalado y actualmente su distribución no ofrece una versión más nueva o no es administrador y no puede cambiar lo que está instalado en ese sistema .Por supuesto, puede descargar el código fuente de bash y crear su propio bash desde cero,
~/bin
por ejemplo. Y también puede modificar su$PATH
variable en su.bash_profile
archivo para incluirla~/bin
como la primera entrada (PATH=$HOME/bin:$PATH
ya~
que no se expandirá$PATH
). Si ahora llamabash
, el shell primero lo buscará$PATH
en orden, por lo que comienza con~/bin
dónde encontrará subash
. Lo mismo sucede si los scripts buscanbash
usar#!/usr/bin/env bash
, por lo que estos scripts ahora estarían funcionando en su sistema usando subash
compilación personalizada .Una desventaja es que esto puede conducir a un comportamiento inesperado, por ejemplo, el mismo script en la misma máquina puede ejecutarse con diferentes intérpretes para diferentes entornos o usuarios con diferentes rutas de búsqueda, causando todo tipo de dolores de cabeza.
El mayor inconveniente
env
es que algunos sistemas solo permitirán un argumento, por lo que no puede hacer esto#!/usr/bin/env <interpreter> <arg>
, ya que los sistemas verán<interpreter> <arg>
como un solo argumento (lo tratarán como si se citara la expresión) y, porenv
lo tanto, buscarán un intérprete llamado<interpreter> <arg>
. Tenga en cuenta que este no es un problema delenv
comando en sí mismo, que siempre permitió pasar múltiples parámetros, pero con el analizador shebang del sistema que analiza esta línea incluso antes de llamarenv
. Mientras tanto, esto se ha solucionado en la mayoría de los sistemas, pero si su script quiere ser ultra portátil, no puede confiar en que esto se haya solucionado en el sistema que ejecutará.Incluso puede tener implicaciones de seguridad, por ejemplo, si
sudo
no se configuró para limpiar el entorno o si$PATH
se excluyó de la limpieza. Déjame demostrarte esto:Por
/bin
lo general, es un lugar bien protegido, soloroot
es capaz de cambiar algo allí. Sin embargo, su directorio de inicio no es ningún programa que ejecute que pueda realizar cambios en él. Eso significa que el código malicioso podría colocar una falsificaciónbash
en algún directorio oculto, modificar su.bash_profile
para incluir ese directorio en su$PATH
, de modo que todos los scripts que se utilicen#!/usr/bin/env bash
terminarán ejecutándose con esa falsificaciónbash
. Si sesudo
mantiene$PATH
, estás en un gran problema.Por ejemplo, considere que una herramienta crea un archivo
~/.evil/bash
con el siguiente contenido:Hagamos un script simple
sample.sh
:Prueba de concepto (en un sistema donde se
sudo
guarda$PATH
):Por lo general, las conchas clásicas deben ubicarse todas
/bin
y si no desea colocarlas allí por cualquier razón, realmente no es un problema colocar un enlace simbólico en/bin
esos puntos a sus ubicaciones reales (o tal vez en/bin
sí mismo es un enlace simbólico), por lo que Yo siempre iría con#!/bin/sh
y#!/bin/bash
. Hay demasiado que se rompería si esto ya no funcionara. No es que POSIX requiera esta posición (POSIX no estandariza los nombres de ruta y, por lo tanto, ni siquiera estandariza la función shebang), sino que son tan comunes que incluso si un sistema no ofreciera un/bin/sh
, probablemente todavía entendería#!/bin/sh
y saber qué hacer con él y solo puede ser por compatibilidad con el código existente.Pero para intérpretes opcionales más modernos, no estándar, como Perl, PHP, Python o Ruby, en realidad no se especifica en ningún lugar donde deberían ubicarse. Pueden estar en
/usr/bin
pero pueden también estar en/usr/local/bin
o en una rama jerarquía completamente diferente (/opt/...
,/Applications/...
, etc.). Es por eso que a menudo usan la#!/usr/bin/env xxx
sintaxis shebang.fuente
Lo encuentro útil, porque cuando no sabía sobre env, antes de comenzar a escribir el script, estaba haciendo esto:
y luego estaba modificando esa línea en el archivo en shebang.
Estaba haciendo esto, porque no siempre recordaba dónde está nodejs en mi computadora: / usr / bin / o / bin /, por lo que para mí
env
es muy útil. Tal vez hay detalles con esto, pero esta es mi razónfuente