La salida de la variable de bucle muestra un valor diferente al esperado

La salida de la variable de bucle muestra un valor diferente al esperado

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 cdingresar 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 cmdmenos los caracteres de nueva línea finales.

Lo que quieres en cambio esexpansión de parámetros: $diro ${dir}(aquí con el parámetro siendo eldir variable).

pwd( print working directory) 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 $PWDsiempre que $PWDno 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

  • echono se puede usar para generar datos arbitrarios, use printfen su lugar
  • generalmente deberías comprobar el estado de salida de los comandos. En este caso, no verificar el resultado de cdsignificaría que lo siguiente cd ..lo llevaría al lugar equivocado si el primero cdfallara.
  • es mejor ejecutar cden 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 cdindica que no puede ingresar a ese */directorio.

En zsh, usarías el Ncalificador glob como en for dir in */(N); do...(o for dir in *(N-/); do...para evitar $dirterminar en /).

En ksh93: for dir in ~(N)*/; do....

En bash, puede configurar la nullglobopción globalmente temporalmente:

shopt -s nullglob
for dir in */; do
  ...
done
shopt -u nullglob

Para completar, $(var)¿es la sintaxis demacroexpansiones en Makefiles tal como las usa el makecomando.

En el awklenguaje, $es un operador unario para desreferenciar campos, y solo se usa varpara hacer referencia a una awkvariable. Entonces, $(var)sería lo mismo que $varo $ vary expandirse al campo varésimo (o todo el registro si vares 0).

En rcshells 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 varvariable, 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 varclave:

$ tclsh
% array set {} {foo 1 bar 2}
% puts $(foo)
1

¹ Sí, Pporque Print no tiene mucho sentido en este contexto. Como puedes adivinar, el pwdcomando (de Unix V5 a mediados de los 70) fue el primero, y $PWDellógicoEl manejo del directorio de trabajo actual, ambos especificados por POSIX, llegó más tarde (en ksh a principios de los 80, donde pwden realidad era un alias para print - $PWD(¡sí, con las -rcomillas que faltan!) y $PWDse denominaba Present Working Directory en la documentación). tcshtiene una variable $cwd( irectorio de trabajo cactual ) para el mismo propósitowd

información relacionada