2009-11-06 13 views
28

estoy intentando escribir una escritura del golpe que va a procesar una lista de archivos cuyos nombres se almacenan uno por línea en un archivo de entrada, algo que los gustos de¿Cómo puedo leer una lista de nombres de archivo de un archivo en bash?

find . -type f -mtime +15 > /tmp/filelist.txt 
for F in $(cat /tmp/filelist.txt) ; do 
    ... 
done; 

Mi problema es que los nombres de archivo en filelist.txt puede contener espacios, por lo que el snipped anteriormente se ampliará la línea

my text file.txt 

a tres nombres de archivo diferentes, my, text y file.txt. ¿Cómo puedo arreglar eso?

+1

Usted sabe, las entradas de directorio también pueden contener saltos de línea, así como espacios y otros personajes extravagantes. Las únicas cosas que una entrada de directorio (nombre de archivo) no puede tener son los caracteres "/" y "". (barra oblicua y nula). – chris

+0

Sí, pero los archivos en los que estoy trabajando son creados por usuarios de Windows en mi LAN que acceden a un recurso compartido de samba, por lo que hay un límite en la rareza del nombre de archivo – agnul

Respuesta

36

Uso read:

while read F ; do 
     echo $F 
done </tmp/filelist.txt 

Alternativamente utilizar IFS para cambiar la forma de la cáscara separa su lista:

OLDIFS=$IFS 
IFS=" 
" 
for F in $(cat /tmp/filelist.txt) ; do 
    echo $F 
done 
IFS=$OLDIFS 

alternativa (como se sugiere por @tangens), convertir el cuerpo de su bucle en una secuencia de comandos por separado, luego use la opción -exec find para ejecutar si para cada archivo encontrado directamente.

+0

Aunque lo voté, encontré que esta secuencia de comandos falla en las rutas que contienen caracteres que no son ASCII. – kakyo

-1

Creo que se puede omitir el archivo temporal en su totalidad y simplemente repetir directamente sobre los resultados de encontrar, es decir .:

for F in $(find . -type f -mtime +15) ; do 
    ... 
done; 

No hay garantías de que mi sintaxis es correcta, pero estoy bastante seguro de que el concepto funciona.

Editar: Si realmente tiene que procesar el archivo con una lista de nombres de archivo y no puede simplemente combinar los comandos como hice anteriormente, puede cambiar el valor de la variable IFS - significa Campo interno Separador: para cambiar cómo bash determina los campos. Por defecto está configurado en espacios en blanco, por lo que una nueva línea, espacio o pestaña comenzará un nuevo campo. Si lo configura para que contenga solo una nueva línea, puede iterar sobre el archivo tal como lo hacía antes.

+1

¿Creo que el shell aún hará separación en el espacio en blanco? –

+0

Vota por favor esto - tiene el MISMO problema que el original (se divide en espacios) – DVK

+0

Hice una edición y tuve que tomar una llamada telefónica en el medio, pero creo que 5 minutos es demasiado largo para esperar en este sitio. – qid

3

uso, mientras leen

echo $FILE | while read line 
do 
echo $line 
done 

que usted puede hacer en lugar de redirigir eco

+0

Si solo tiene un archivo, una redirección de shell evita generar un proceso 'cat'. –

0

No soy un experto golpe por cualquier medio (por lo general escribo mi script en Ruby o Python para ser multiplataforma), pero usaría una expiración de expresiones regulares para escapar espacios en cada línea antes de procesarla.

Para Bash Regex: http://www.linuxjournal.com/node/1006996

En una situación similar en Rubí (procesar un archivo csv, y la limpieza de cada línea antes de usarlo):

File.foreach(csv_file_name) do |line| 
    clean_line = line.gsub(/()/, '\ ') 
    #this finds the space in your file name and escapes it  
    #do more stuff here 
end 
1

se puede utilizar el parámetro -exec de find y utilizar los nombres de los archivos directamente:

find . -type f -mtime +15 -exec <your command here> {} \; 

El {} es un marcador de posición para el nombre del archivo.

+0

El problema es que no estoy ejecutando un solo comando y terminaría probando todo tipo de combinaciones extrañas para encontrar la forma correcta de citar y escapar de las cosas después de -exec – agnul

6

Puede hacer esto sin un archivo temporal utilizando proceso de sustitución:

while read F 
do 
    ... 
done < <(find . -type f -mtime +15) 
+0

¡Aseado! No sabía acerca de la sustitución del proceso. – agnul

+1

Tenga en cuenta que la función de sustitución de proceso es una extensión bash, y ni siquiera está disponible con bash en el modo de compatibilidad sh. Debe comenzar su script con '#!/Bin/bash' para que funcione. Por cierto, también recomiendo usar 'while IFS =" "read -r F' para evitar posibles problemas con el espacio en blanco al principio o al final de los nombres de archivos, y barras diagonales inversas al final (aunque si los archivos provienen de Windows, las barras invertidas probablemente no sean posible). –

1

tubería de su comando find recta para pasar leer bucle

find . -type f -mtime +15 | while read -r line 
do 
    printf "do something with $line\n" 
done 
Cuestiones relacionadas