Cuando abro un indicador de bash y escribo:
$ set -o xtrace
$ x='~/someDirectory'
+ x='~/someDirectory'
$ echo $x
+ echo '~/someDirectory'
~/someDirectory
Esperaba que la quinta línea de arriba se hubiera ido + echo /home/myUsername/someDirectory
. ¿Hay alguna forma de hacer esto? En mi script Bash original, la variable x en realidad se está rellenando a partir de datos de un archivo de entrada, a través de un bucle como este:
while IFS= read line
do
params=($line)
echo ${params[0]}
done <"./someInputFile.txt"
Aún así, estoy obteniendo un resultado similar, con el en echo '~/someDirectory'
lugar de echo /home/myUsername/someDirectory
.
x='~'; print -l ${x} ${~x}
. Me di por vencido después de revisar elbash
manual por un tiempo.Respuestas:
El estándar POSIX impone que la expansión de palabras se realice en el siguiente orden (el énfasis es mío):
El único punto que nos interesa aquí es el primero: como puede ver, la expansión de tilde se procesa antes de la expansión de parámetros:
echo $x
, no se encuentra tilde, por lo que continúa.echo $x
,$x
se encuentra y se expande y la línea de comando se convierteecho ~/someDirectory
.~
personaje permanece tal cual.Al usar las comillas mientras asignaba
$x
, solicitaba explícitamente no expandir la tilde y tratarla como un carácter normal. Una cosa que a menudo se pasa por alto es que en los comandos de shell no tiene que citar la cadena completa, por lo que puede hacer que la expansión se realice correctamente durante la asignación de variables:Y también puede hacer que la expansión ocurra en la
echo
línea de comandos siempre que pueda ocurrir antes de la expansión de parámetros:Si por alguna razón realmente necesita afectar la tilde a la
$x
variable sin expansión, y poder expandirla con elecho
comando, debe proceder dos veces para forzar$x
que ocurran dos expansiones de la variable:Sin embargo, tenga en cuenta que, según el contexto en el que utilice dicha estructura, puede tener efectos secundarios no deseados. Como regla general, prefiera evitar usar cualquier cosa que requiera
eval
cuando tenga otra forma.Si desea abordar específicamente el problema de tilde en lugar de cualquier otro tipo de expansión, dicha estructura sería más segura y portátil:
Esta estructura verifica explícitamente la presencia de un líder
~
y lo reemplaza con el directorio de inicio del usuario si se encuentra.Después de su comentario,
x="${HOME}/${x#"~/"}"
puede ser sorprendente para alguien que no se utiliza en la programación de shell, pero de hecho está vinculado a la misma regla POSIX que cité anteriormente.Según lo impuesto por el estándar POSIX, la eliminación de comillas ocurre en último lugar y la expansión de parámetros ocurre muy temprano. Por lo tanto,
${#"~"}
se evalúa y se expande mucho antes de la evaluación de las comillas externas. Por turnos, como se define en las reglas de expansión de parámetros :Por lo tanto, el lado derecho del
#
operador debe ser citado o escapado correctamente para evitar la expansión de la tilde.Entonces, para decirlo de manera diferente, cuando el intérprete de shell mira
x="${HOME}/${x#"~/"}"
, ve:${HOME}
y${x#"~/"}
debe ser expandido.${HOME}
se expande al contenido de la$HOME
variable.${x#"~/"}
desencadena una expansión anidada:"~/"
se analiza pero, al citarse, se trata como un literal 1 . Podría haber usado comillas simples aquí con el mismo resultado.${x#"~/"}
la expresión en sí ahora se expande, lo que hace que el prefijo~/
se elimine del valor de$x
.${HOME}
, el literal/
, la expansión${x#"~/"}
.a=$b
, generalmente encuentro más claro agregar comillas dobles.Por cierto, si observa más de cerca la
case
sintaxis, verá la"~/"*
construcción que se basa en el mismo conceptox=~/'someDirectory'
que expliqué anteriormente (aquí nuevamente, las comillas dobles y simples podrían usarse indistintamente).No se preocupe si estas cosas pueden parecer oscuras a primera vista (¡tal vez incluso a la segunda o más tarde!). En mi opinión, la expansión de parámetros es, con subcapas, uno de los conceptos más complejos a la hora de programar en lenguaje shell.
Sé que algunas personas pueden estar en total desacuerdo, pero si desea aprender más a fondo sobre la programación de shell, le animo a leer la Guía avanzada de secuencias de comandos Bash : enseña la secuencia de comandos Bash, así que con muchas extensiones y campanas. silbidos en comparación con las secuencias de comandos de shell POSIX, pero lo encontré bien escrito con muchos ejemplos prácticos. Una vez que maneje esto, es fácil restringirse a las funciones POSIX cuando lo necesite, personalmente creo que ingresar directamente en el reino POSIX es una curva de aprendizaje empinada innecesaria para principiantes (compare mi reemplazo de tilde POSIX con Bash tipo regex de @ m0dular equivalente a tener una idea de lo que quiero decir;)!).
1 : Lo que me lleva a encontrar un error en Dash que no implementa la expansión de tilde aquí correctamente (uso comprobable
x='~/foo'; echo "${x#~/}"
). ¡La expansión de parámetros es un campo complejo tanto para el usuario como para los propios desarrolladores de shell!fuente
x="${HOME}/${x#"~/"}"
? Se ve como una concatenación de 3 cuerdas:"${HOME}/${x#"
,~/
, y"}"
. ¿El shell permite comillas dobles anidadas cuando el par interno de comillas dobles está dentro de un${ }
bloque?Una posible respuesta:
Como estás leyendo la entrada de un archivo, no haría esto.
Puede buscar y reemplazar el ~ con el valor de $ HOME, así:
Me da
fuente
${parameter/pattern/string}
expansión es una extensión Bash y puede no estar disponible en otros shells.case
estructura POSIX ilustra bien cómo las secuencias de comandos Bash son más fáciles de usar especialmente para principiantes.