2009-08-28 17:03:49 +0000 2009-08-28 17:03:49 +0000
101
101

haciendo un bucle a través de &007 resulta en un script bash shell

¿Alguien tiene un script shell de plantilla para hacer algo con ls para una lista de nombres de directorios y haciendo un bucle a través de cada uno y haciendo algo?

Estoy planeando hacer ls para obtener la lista de nombres de directorios.

Respuestas (7)

120
120
120
2009-08-28 17:09:37 +0000

Editado para no usar ls donde un globo haría, como @shawn-j-goff y otros sugirieron.

Sólo usa un bucle for..do..done:

for f in *; do
  echo "File -> $f"
done

Puedes reemplazar el * por *.txt o cualquier otro globo que devuelva una lista (de archivos, directorios, o cualquier cosa para el caso), un comando que genere una lista, por ejemplo, $(cat filelist.txt), o en realidad reemplazarlo con una lista.

Dentro del bucle do, sólo te refieres a la variable de bucle con el prefijo del signo de dólar (así $f en el ejemplo anterior). Puedes echo o hacer cualquier otra cosa que quieras.

Por ejemplo, para renombrar todos los archivos .xml del directorio actual a .txt:

for x in *.xml; do 
  t=$(echo $x | sed 's/\.xml$/.txt/'); 
  mv $x $t && echo "moved $x -> $t"
done

O incluso mejor, si usas Bash puedes usar expansiones del parámetro Bash en lugar de generar una subcarpeta:

for x in *.xml; do 
  t=${x%.xml}.txt
  mv $x $t && echo "moved $x -> $t"
done
69
69
69
2012-04-29 22:16:24 +0000

Usar la salida de ls para obtener nombres de archivos es una mala idea. Puede conducir a un mal funcionamiento e incluso a guiones peligrosos. Esto se debe a que un nombre de archivo puede contener cualquier carácter excepto / y el carácter null, y ls no utiliza ninguno de esos caracteres como delimitadores, por lo que si un nombre de archivo tiene un espacio o una nueva línea, _obtendrá resultados inesperados.

Hay dos formas muy buenas de iterar sobre los archivos. Aquí, he usado simplemente echo para demostrar cómo hacer algo con el nombre de archivo; puedes usar cualquier cosa, sin embargo.

La primera es usar las características nativas de globbing de la shell.

for dir in */; do
  echo "$dir"
done

El shell expande */ en argumentos separados que el bucle for lee; incluso si hay un espacio, una nueva línea o cualquier otro carácter extraño en el nombre de archivo, for verá cada nombre completo como una unidad atómica; no está analizando la lista de ninguna manera.

Si quieres ir de forma recursiva a subdirectorios, entonces esto no servirá a menos que tu shell tenga algunas características de globbing extendidas (como las de bash globstar. Si tu shell no tiene estas características, o si quieres asegurarte de que tu script funcionará en una variedad de sistemas, entonces la siguiente opción es usar find.

find . -type d -exec echo '{}' \;

Aquí, el comando find llamará a echo y le pasará un argumento del nombre de archivo. Lo hace una vez por cada archivo que encuentra. Al igual que en el ejemplo anterior, no hay análisis sintáctico de una lista de nombres de archivos; en su lugar, se envía un nombre de archivo completamente como un argumento.

La sintaxis del argumento -exec tiene un aspecto un poco extraño. find toma el primer argumento después de -exec y lo trata como el programa a ejecutar, y cada argumento posterior, lo toma como un argumento a pasar a ese programa. Hay dos argumentos especiales que -exec necesita ver. El primero es {}; este argumento es reemplazado por un nombre de archivo que las partes anteriores de find generan. El segundo es ;, que permite a find saber que éste es el final de la lista de argumentos que hay que pasar al programa; find necesita esto porque puedes continuar con más argumentos que están destinados a find y que no están destinados al programa ejecutado. La razón de [Usar la salida delspara obtener nombres de archivos es una mala idea] &003. Puede conducir a un mal funcionamiento e incluso a guiones peligrosos. Esto se debe a que un nombre de archivo puede contener cualquier carácter excepto/y el carácternull, yls` no utiliza ninguno de esos caracteres como delimitadores, por lo que si un nombre de archivo tiene un espacio o una nueva línea, _obtendrá resultados inesperados.

Hay dos formas muy buenas de iterar sobre los archivos. Aquí, he usado simplemente echo para demostrar cómo hacer algo con el nombre de archivo; puedes usar cualquier cosa, sin embargo.

La primera es usar las características nativas de globbing de la shell.

for dir in */; do
  echo "$dir"
done

El shell expande */ en argumentos separados que el bucle for lee; incluso si hay un espacio, una nueva línea o cualquier otro carácter extraño en el nombre de archivo, for verá cada nombre completo como una unidad atómica; no está analizando la lista de ninguna manera.

Si quieres ir de forma recursiva a subdirectorios, entonces esto no servirá a menos que tu shell tenga algunas características de globbing extendidas (como las de bash globstar. Si tu shell no tiene estas características, o si quieres asegurarte de que tu script funcionará en una variedad de sistemas, entonces la siguiente opción es usar find.

find . -type d -exec echo '{}' \;

Aquí, el comando find llamará a echo y le pasará un argumento del nombre de archivo. Lo hace una vez por cada archivo que encuentra. Al igual que en el ejemplo anterior, no hay análisis sintáctico de una lista de nombres de archivos; en su lugar, se envía un nombre de archivo completamente como un argumento.

La sintaxis del argumento -exec tiene un aspecto un poco extraño. find toma el primer argumento después de -exec y lo trata como el programa a ejecutar, y cada argumento posterior, lo toma como un argumento a pasar a ese programa. Hay dos argumentos especiales que -exec necesita ver. El primero es {}; este argumento es reemplazado por un nombre de archivo que las partes anteriores de find generan. El segundo es ;, que permite a find saber que éste es el final de la lista de argumentos que hay que pasar al programa; find necesita esto porque puedes continuar con más argumentos que están destinados a find y que no están destinados al programa ejecutado. La razón de es que la shell también trata ; de forma especial - representa el final de un comando, por lo que necesitamos escapar de él para que la shell lo dé como argumento a find en lugar de consumirlo por sí mismo; otra forma de conseguir que la shell no lo trate de forma especial es ponerlo entre comillas: ';' funciona igual de bien que \; para este propósito.

17
17
17
2009-08-28 17:26:27 +0000

Para los archivos con espacios en blanco tendrás que asegurarte de citar la variable como:

for i in $(ls); do echo "$i"; done;

o, puedes cambiar la variable de entorno del separador de campo de entrada (IFS):

IFS=$'\n';for file in $(ls); do echo $i; done

Finalmente, dependiendo de lo que estés haciendo, puede que ni siquiera necesites el ls:

for i in *; do echo "$i"; done;
5
5
5
2010-08-22 20:02:35 +0000

Si tienes instalado GNU Paralelo http://www.gnu.org/software/parallel/ puedes hacer esto:

ls | parallel echo {} is in this dir

Para renombrar todos los .txt a .xml:

ls *.txt | parallel mv {} {.}.xml

Mira el video introductorio de GNU Paralelo para saber más http://www.youtube.com/watch?v=OpaiGYxkSuQ

4
4
4
2015-02-11 14:36:01 +0000

Sólo para añadir a la respuesta de CoverosGene, aquí hay una forma de listar sólo los nombres de los directorios:

for f in */; do
  echo "Directory -> $f"
done
1
1
1
2012-05-10 22:36:39 +0000

¿Por qué no poner el IFS en una nueva línea, y luego capturar la salida de ls en un array? Configurar IFS a newline debería resolver los problemas con los caracteres divertidos en los nombres de los archivos; usar ls puede ser agradable porque tiene una función de clasificación incorporada.

(En las pruebas tuve problemas para configurar IFS a \n, pero configurarlo a newline backspace funciona, como se sugiere en otra parte de este documento):

Por ejemplo, (asumiendo que el patrón de búsqueda deseado de ls pasó en $1):

SAVEIFS=$IFS
IFS=$(echo -en "\n\b")

FILES=($(/bin/ls "$1"))

for AFILE in ${FILES[@]}
do
    ... do something with a file ...
done

IFS=$SAVEIFS

Esto es especialmente útil en OS X, por ejemplo, para capturar una lista de archivos ordenados por fecha de creación (de más antiguo a más reciente), el comando ls es ls -t -U -r.

-3
-3
-3
2009-08-28 17:28:24 +0000

Así es como lo hago, pero probablemente hay formas más eficientes.

ls > filelist.txt

while read filename; do echo filename: "$filename"; done < filelist.txt