Lista de elementos con espacios en zsh

10

He estado estudiando secuencias de comandos zsh durante 2 horas en este momento y me he topado con una pared. Quiero ir a través de una lista de archivos que pueden tener espacios en ellos. Estoy abierto a enfoques completamente diferentes que el siguiente ejemplo, siempre que sean zsh ya que zsh es lo que estoy estudiando, no la tarea que estoy tratando de escribir.

files=(`ls`)
for f in $files; do
    print $f
done

Obviamente no solo estoy recreando ls, solo quiero $fcapturar el nombre completo del archivo en cada iteración del bucle.

Felix
fuente

Respuestas:

12

Primero, supongo que el uso de lses solo un ejemplo. No puede analizar la salida de lsen ningún shell, porque es ambiguo. Lea Por qué no debe analizar la salida de ls (1) si esto es nuevo para usted. En cualquier shell, para obtener una lista de archivos, use comodines, por ejemplo files=(*).

En zsh, como en otros shells, el resultado de la sustitución de comandos se divide en palabras en los espacios en blanco (más precisamente, de acuerdo con el valor de IFS). (A diferencia de otros shells, el resultado de la sustitución del comando no está sujeto a glob en zsh). Entonces, si la salida del lscomando es

hello world
wibble

a continuación, files=($(ls))coloca la filesmatriz para contener 3 elementos: hello, worldy wibble.

Si la sustitución del comando está entre comillas dobles, no se realiza ninguna división. Puede realizar una división personalizada con banderas de expansión de parámetros . Use el @indicador para indicar que el resultado de la división es una matriz (curiosamente, debe mantener la expansión entre comillas dobles, es decir "${(@)…}", aunque la cadena entre comillas dobles se expanda a varias palabras). Para dividir, use la sbandera, por ejemplo, "${(@s:,:)…}"para dividir en comas; la fbandera se divide solo en líneas nuevas.

files=("${(@f)$(ls)}")

Tenga en cuenta que la forma correcta de iterar sobre una matriz en general es for f in $files[@], al $filesquitar los elementos vacíos (aquí, no importa porque los elementos no estarán vacíos).

print $finterpreta $fcomo un interruptor si comienza con -ay expande las barras invertidas $f. Use print -r -- $f, o print -rn -- $fsi no desea agregar una nueva línea después de la cadena.

Gilles 'SO- deja de ser malvado'
fuente
Gracias. Sí, fue solo un ejemplo. No había un objetivo específico en mente en este momento, solo quiero saber cómo citar o escapar de los elementos de la lista de cualquier comando que pueda tener resultados individuales con espacios en blanco en ellos.
Felix
Una cosa que encuentro confusa es ¿por qué la expresión externa () en sus archivos = ("$ {(@ f) $ (ls)}") es necesaria con la @f? Y solo jugar (f) parece funcionar bien siempre que () aparezca en el exterior.
Koobz
@Koobz 1. Los externos (…)son necesarios para que filessea ​​una matriz y no una cadena (que contendría la concatenación de las líneas del comando, deshaciendo la división realizada por (f)). 2. Con foo=$'one\n\nthree', contrast print -rl $ {(f) foo} `y print "${(@f)foo}". Las comillas dobles son necesarias para retener líneas vacías, de las cuales no obtendrá lspero que pueden ocurrir con otros comandos.
Gilles 'SO- deja de ser malvado'
Muy apreciado. ¿Hay alguna rima o razón para esta locura? Incluso allí, lo que es confuso es por qué el exterior () simplemente vuelve a dividir la cadena en palabras individuales nuevamente si el valor intermedio es mi cadena concatenada. Esto parece una interpolación de cadenas en ruby ​​o php, por ejemplo.
Koobz
@Koobz La razón del tratamiento especial de las palabras vacías es la compatibilidad histórica con versiones antiguas de zsh olvidadas hace mucho tiempo. La razón para no dividirse automáticamente es que es un comportamiento sorprendente y a menudo no deseable. La división automática es un comportamiento del shell Bourne que zsh no emula a menos que se lo indique.
Gilles 'SO- deja de ser malvado'
2

En zsh puede usar la expansión del shell que no realiza la división de palabras por defecto. Tratar

for f in /path/to/files/*; do
    print ${f}
done
u-punkt
fuente