Quiero hacer algo repetidamente en una lista de archivos. Los archivos en las preguntas tienen espacios en sus nombres:
david@david: ls -l
total 32
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha
-rw-rw-r-- 1 david david 0 Mai 8 11:55 haha~
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (3rd copy)
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (4th copy)
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (5th copy)
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (6th copy)
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (7th copy)
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (another copy)
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (copy)
Ahora quiero anotar cada uno de estos archivos:
david@david: echo '
for file in $(ls)
do
stat $file
done' | bash
(Uso echo y una tubería para escribir comandos de varias líneas).
Cuando hago eso, funciona correctamente en aquellos archivos que no tienen espacios en sus nombres. Pero los otros ...
stat: cannot stat ‘(another’: No such file or directory
stat: cannot stat ‘copy)’: No such file or directory
Cambiar $(ls)
a "$(ls)"
o $file
para "$file"
no funciona. ¿Que puedo hacer?
Editar:
echo '
for files in *
do
stat "$files"
done' | bash
¡Hace el truco! Como soy nuevo en bash, quiero mantener las cosas lo más simples posible, así que nada con tratar de escapar de espacios, o usar xargs
o la solución read -r
, aunque resuelvan el problema.
Como algunos han preguntado: Sí, usar esto en lugar de stat *
es extraño. Pero solo quería encontrar una forma general de aplicar el mismo comando en un grupo de nombres de archivo en bash, usando un bucle for. Entonces stat
podría significar gzip
, gpg
o rm
.
fuente
stat *
? (;-)Respuestas:
La cita múltiple de la
echo '
está complicando la cosa.Solo puedes usar:
Pero también
... y si desea recopilar los archivos y luego aplicar el comando (¿por qué?) puede ir con (pero tenga cuidado con el archivo que contiene nuevas líneas ... (1))
... y si también quieres archivos ocultos, solo úsalo
* .*
como un patrón, pero luego recuerda eso.
y..
estará en el conjunto .Como comentario aparte, no debe analizar la
ls
salida .(1) pero si tiene nombres de archivo con nuevas líneas, de alguna manera se lo merece ... ;-)
fuente
for f in *; do command "$f"; done
. Nunca analicels
, ciertamente nunca lo haga en unfor
bucle y ¿por qué usarloecho
?do
,|
,&&
etc, puede continuar en la nueva línea. O eso o usa heredocs. No hay razón para usarecho
y también puede causar problemas.En una nota al margen: puede dividir comandos largos / complicados en varias líneas agregando un espacio seguido de una barra diagonal inversa y presionando Entercada vez que desee comenzar a escribir en una nueva línea, en lugar de bifurcar múltiples procesos usando
echo [...] | bash
; También debe encerrar$file
entre comillas dobles, para evitar que sestat
rompan en caso de nombres de archivo que contengan espacios:El problema es que se
$(ls)
expande a una lista de nombres de archivos que contienen espacios, y lo mismo ocurrirá también con"$(ls)"
.Incluso resolviendo este problema, este método aún se romperá en los nombres de archivo que contengan barras diagonales inversas y en los nombres de archivo que contengan nuevas líneas (como lo indica terdon).
Una solución para ambos problemas sería canalizar la salida de
find
unwhile
bucle en ejecuciónread -r
para que, en cada iteración,read -r
almacene una línea defind
salida en$file
:fuente
ls
. Siempre.ls
. Hay formas mejores y más robustas. También es posible que desee leer esto: ¿Por qué * no * analiza `ls`? para más detalles.ls
, esfor
-for
itera sobre palabras (separadas por IFS) dadas después de lain
palabra clave`for
yls
.for f in *
estaría bien, por ejemplo.Usa lo bueno
find
, funciona con archivos ocultos, líneas nuevas y espacios.o cualquier otro en lugar de
stat
fuente
Como un chico R, ya encontré una solución en R:
Lo sé, es una locura. Deseo que la salida de
ls
sea más fácil de analizar ... R puede tratar con espacios, porque dir () devuelve un valor de carácter entre comillas. Cualquier cosa entre las comillas es un nombre de archivo válido con espacios.fuente
for f in *
, presione enter y continúe en una nueva línea:do
presione enter nuevamentestat "$f"
, ingrese nuevamente,done
ingrese. Ese es un comando dividido muy bien en 4 líneas y no se romperá en ningún tipo de nombre de archivo.Me he encontrado con otras instancias de problemas de espacios en blanco en bucles for, por lo que el siguiente comando (imo más robusto) es lo que generalmente uso. También se adapta muy bien a las tuberías.
Puede combinar esto con
grep
o usarfind
en su lugar:fuente
-E
opcióngrep
, sin la cual no se reconocerá+
en una expresión regular. Incluso entonces, es un cambio y una falla en esta pregunta, ya quegrep
elimina los nombres de archivos que contienen espacios . También elimina los nombres de archivo que contienen dígitos (números) y signos de puntuación (por ejemplo, paréntesis), como lo hacen los ejemplos en la pregunta. Y eso ni siquiera menciona nombres de archivos que contengan nueva línea o que comiencen con-
(guión).Esta respuesta resolverá el problema del análisis
ls
y cuidará los espacios de retroceso y las nuevas líneas.Pruebe esto, resolverá su problema usando el separador de campo interno IFS.
Pero también puede resolverlo fácilmente sin necesidad de analizar la salida de ls utilizando
fuente
IFS
aquí: citar la variable debería ser suficiente para evitar la división de palabras, ¿verdad?ls
.En cambio, puede cambiar el nombre de sus archivos reemplazando el espacio con algún otro carácter como el guión bajo, para deshacerse de este problema:
Para hacerlo, ejecute fácilmente el comando:
fuente