¿Alguien tiene un script de shell de plantilla para hacer algo con ls
una lista de nombres de directorio y recorrer cada uno y hacer algo?
Estoy planeando hacer ls -1d */
para obtener la lista de nombres de directorio.
¿Alguien tiene un script de shell de plantilla para hacer algo con ls
una lista de nombres de directorio y recorrer cada uno y hacer algo?
Estoy planeando hacer ls -1d */
para obtener la lista de nombres de directorio.
Respuestas:
Editado para no usar ls donde lo haría un globo, como sugirieron @ shawn-j-goff y otros.
Solo usa un for..do..done
bucle:
for f in *; do
echo "File -> $f"
done
Puede reemplazar el *
con *.txt
o cualquier otro pegote que devuelve una lista (de los archivos, directorios o lo que fuera), un comando que genera una lista, por ejemplo, $(cat filelist.txt)
o en realidad sustituirla por una lista.
Dentro del do
bucle, solo se refiere a la variable del bucle con el prefijo del signo de dólar ( $f
en el ejemplo anterior). Puedes echo
hacerlo o hacer cualquier otra cosa que quieras.
Por ejemplo, para cambiar el nombre de todos los .xml
archivos en el 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 está usando Bash, puede usar expansiones de parámetros de Bash en lugar de generar una subshell:
for x in *.xml; do
t=${x%.xml}.txt
mv $x $t && echo "moved $x -> $t"
done
for
bucles, y un error típico de tratar de analizar la salida de ls
. @ DanielA.White, podría considerar no aceptar esta respuesta si no fue útil (o es potencialmente engañosa), ya que, como dijo, está actuando en los directorios. La respuesta de Shawn J. Goff debería proporcionar una solución más sólida y funcional a su problema.
ls
salida , no debe leer la salida en un for
bucle , debe usar en $()
lugar de `` y Usar más comillas ™ . Estoy seguro de que @CoverosGene tenía buenas intenciones, pero esto es simplemente terrible.
ls
es ls -1 | while read line; do stuff; done
. Al menos ese no se romperá por espacios en blanco.
mv -v
usted no necesitaecho "moved $x -> $t"
Usar la salida de ls
para obtener nombres de archivos es una mala idea . Puede provocar un mal funcionamiento e incluso scripts peligrosos. Esto se debe a un nombre de archivo puede contener cualquier carácter excepto /
y el null
carácter, y ls
no utilizar cualquiera de esos caracteres como delimitadores, por lo que si un nombre de archivo tiene un espacio o una nueva línea, que va a obtener resultados inesperados.
Hay dos formas muy buenas de iterar sobre archivos. Aquí, he usado simplemente echo
para demostrar que estoy haciendo algo con el nombre del archivo; Sin embargo, puedes usar cualquier cosa.
El primero es usar las características de globbing nativas del shell.
for dir in */; do
echo "$dir"
done
El shell se expande */
en argumentos separados que for
lee el bucle; incluso si hay un espacio, una nueva línea o cualquier otro carácter extraño en el nombre del archivo, for
verá cada nombre completo como una unidad atómica; no está analizando la lista de ninguna manera.
Si desea ir recursivamente a subdirectorios, entonces esto no funcionará a menos que su shell tenga algunas características de globbing extendidas (como bash
's globstar
. Si su shell no tiene estas características, o si quiere asegurarse de que su script funcionará en una variedad de sistemas, entonces la siguiente opción es usar find
.
find . -type d -exec echo '{}' \;
Aquí, el find
comando llamará echo
y le pasará un argumento del nombre del archivo. Hace esto una vez por cada archivo que encuentra. Al igual que con el ejemplo anterior, no se analiza una lista de nombres de archivo; en cambio, un fileneame se envía completamente como argumento.
La sintaxis del -exec
argumento parece un poco divertida. find
toma el primer argumento después -exec
y lo trata como el programa a ejecutar, y cada argumento posterior, toma como un argumento para pasar a ese programa. Hay dos argumentos especiales que -exec
hay que ver. El primero es {}
; este argumento se reemplaza con un nombre de archivo que find
genera las partes anteriores . El segundo es ;
, que permite find
saber que este es el final de la lista de argumentos para pasar al programa; find
necesita esto porque puede continuar con más argumentos destinados find
y no destinados al programa ejecutado. La razón de esto \
es que la cáscara también trata;
especialmente: representa el final de un comando, por lo que debemos escapar para que el shell lo ofrezca como argumento en find
lugar de consumirlo por sí mismo; Otra forma de hacer que el shell no lo trate especialmente es ponerlo entre comillas: ';'
funciona tan bien como \;
para este propósito.
find
. Hay magia que se puede hacer, e incluso las limitaciones percibidas -exec
se pueden solucionar. La -print0
opción también es valiosa para usar con xargs
.
Para los archivos con espacios, deberá asegurarse de citar la variable como:
for i in $(ls); do echo "$i"; done;
o puede 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é haciendo, es posible que ni siquiera necesite el ls:
for i in *; do echo "$i"; done;
\n
es insuficiente. Nunca es una buena idea recomendar analizar la salida de ls
.
Si tiene instalado GNU Parallel http://www.gnu.org/software/parallel/ , puede hacer esto:
ls | parallel echo {} is in this dir
Para cambiar el nombre de todo .txt a .xml:
ls *.txt | parallel mv {} {.}.xml
Mire el video de introducción de GNU Parallel para obtener más información: http://www.youtube.com/watch?v=OpaiGYxkSuQ
¿Por qué no establecer IFS en una nueva línea y luego capturar la salida de ls
en una matriz? Establecer IFS en nueva línea debería resolver problemas con caracteres divertidos en los nombres de archivo; usar ls
puede ser bueno porque tiene una función de clasificación incorporada.
(En las pruebas, tuve problemas para configurar IFS \n
pero funciona como backspace de nueva línea, como se sugiere en otro lugar aquí):
Por ejemplo (suponiendo ls
que se pasa el patrón de búsqueda deseado $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 (más antigua a más reciente), el ls
comando es ls -t -U -r
.
\n
es insuficiente. Las únicas soluciones válidas utilizan un bucle for con expansión de nombre de ruta, o find
.
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
ls | while read i; do echo filename: $i; done