
Imagine que tengo dos carpetas en mi directorio de trabajo actual: back/
y front/
. Estoy tratando de entrar dentro de cada uno de ellos y hacer algunas cosas. Del sencillo script a continuación:
for dir in */ ; do
echo "$(dir)"
done
Esperaba un resultado como:
back
front
Sin embargo, lo que obtengo es:
back front
back front
No entiendo. La consecuencia es que, si intento cd
ingresar a la carpeta:
for dir in */ ; do
echo "$(dir)"
cd $(dir)
echo "$(pwd)"
cd ..
done
Yo obtengo:
back front
/path/to/back
back front
/path/to/back
Es decir, nunca entro front/
. ¿Alguien podría ayudarme a entender qué estoy haciendo mal?
Respuesta1
En shells tipo POSIX, $(cmd)
¿es la sintaxis parasustitución de comando, $(cmd)
se expande a la salida de cmd
menos los caracteres de nueva línea finales.
Lo que quieres en cambio esexpansión de parámetros: $dir
o ${dir}
(aquí con el parámetro siendo eldir
variable).
pwd
( p
rint w
orking d
irectory) es el comando que genera el contenido de la $PWD
¹ variable especial que a su vez contiene una ruta al directorio de trabajo actual, por lo que $(pwd)
se expande a lo mismo $PWD
siempre que $PWD
no termine en caracteres de nueva línea.
Entonces quieres:
for dir in */; do
(
printf '%s\n' "$dir"
cd -- "$dir" &&
printf '%s\n' "$PWD"
)
done
O:
for dir in */; do
(
printf '%s\n' "$dir"
cd -- "$dir" &&
pwd
)
done
Recuerda también que
echo
no se puede usar para generar datos arbitrarios, useprintf
en su lugar- generalmente deberías comprobar el estado de salida de los comandos. En este caso, no verificar el resultado de
cd
significaría que lo siguientecd ..
lo llevaría al lugar equivocado si el primerocd
fallara. - es mejor ejecutar
cd
en un subshell (usando(...)
) en lugar de usar,cd ..
ya que no siempre se garantiza que este último lo llevará de regreso al lugar inicial (si hay enlaces simbólicos involucrados y/o algunos componentes de la ruta han sido renombrados en el intervalo) --
debe usarse para separar las opciones de las que no son opciones cuando no se puede garantizar que las no opciones no comiencen-
(o+
lo que también puede ser un problema para algunos comandos).- en shells tipo POSIX, las expansiones de parámetros y las sustituciones de comandos (y las expansiones aritméticas) deben citarse, de lo contrario se someten a división+glob implícita (excepto en zsh) que generalmente no desea.
También tenga en cuenta que en bash (y otros shells POSIX excepto zsh), cuando un glob no coincide con ningún archivo, se deja sin expandir.
Entonces, si el directorio de trabajo actual no contiene ningún archivo no oculto que pueda determinarse como del tipodirectorio, el */
patrón global simplemente se expandirá a sí mismo */
en lugar de una lista vacía y probablemente verá un error que cd
indica que no puede ingresar a ese */
directorio.
En zsh
, usarías el N
calificador glob como en for dir in */(N); do...
(o for dir in *(N-/); do...
para evitar $dir
terminar en /
).
En ksh93: for dir in ~(N)*/; do...
.
En bash
, puede configurar la nullglob
opción globalmente temporalmente:
shopt -s nullglob
for dir in */; do
...
done
shopt -u nullglob
Para completar, $(var)
¿es la sintaxis demacroexpansiones en Makefile
s tal como las usa el make
comando.
En el awk
lenguaje, $
es un operador unario para desreferenciar campos, y solo se usa var
para hacer referencia a una awk
variable. Entonces, $(var)
sería lo mismo que $var
o $ var
y expandirse al campo var
ésimo (o todo el registro si var
es 0).
En rc
shells tipo -, $(var)
lo mismo que $var
, $ var
, $ 'var'
, $ ( 'var' )
también funcionaría, ya que es una lista de un elemento pasado a $
, por lo que estás desreferenciando la var
variable, aunque normalmente usarías solo $var
.
En TCL, $(var)
se expandiría al valor de la variable de matriz con un nombre vacío para la var
clave:
$ tclsh
% array set {} {foo 1 bar 2}
% puts $(foo)
1
¹ Sí, P
porque P
rint no tiene mucho sentido en este contexto. Como puedes adivinar, el pwd
comando (de Unix V5 a mediados de los 70) fue el primero, y $PWD
ellógicoEl manejo del directorio de trabajo actual, ambos especificados por POSIX, llegó más tarde (en ksh a principios de los 80, donde pwd
en realidad era un alias para print - $PWD
(¡sí, con las -r
comillas que faltan!) y $PWD
se denominaba P
resent W
orking D
irectory en la documentación). tcsh
tiene una variable $cwd
( irectorio de trabajo c
actual ) para el mismo propósitow
d