2009-10-30 18 views
6

Estoy intentando recorrer un directorio de archivos de texto y combinarlos en un solo documento. Esto funciona muy bien, pero los archivos de texto contienen fragmentos de código, y todo mi formato está colapsado hacia la izquierda. Se eliminan todos los espacios en blanco iniciales en una línea.Preservar el espacio en blanco al inicio mientras se lee >> escribir un archivo línea por línea en bash

#!/bin/sh 
OUTPUT="../best_practices.textile" 
FILES="../best-practices/*.textile" 
for f in "$FILES" 
do 
    echo "Processing $f file..." 
    echo "">$OUTPUT 

    cat $f | while read line; do 
     echo "$line">>$OUTPUT 
    done 
    echo >>$OUTPUT 
    echo >>$OUTPUT 
done 

Soy sinceramente un principiante bash, pero después de buscar alto y bajo no pude encontrar una solución adecuada. Aparentemente BASH odia el espacio blanco líder en general.

Respuesta

3

En lugar de:

cat $f | while read line; do 
    echo "$line">>$OUTPUT 
done 

Haga esto:

cat $f >>$OUTPUT 

(. Si hay una razón por la que tiene que hacer línea de cosas por la línea que sería bueno incluir eso en la pregunta)

+0

Eso mata el espacio en blanco también. Cambié a línea por línea para ver si tal vez me daría más opciones para guardar el espacio principal. –

+1

d'oh NO mata el espacio en blanco. Gracias hombre:> –

+1

Interesante. Esta respuesta ha sido downvoted dos veces sin explicación. Si va a downvote, diga por qué. (Y si es porque estás pensando "esto podría ser solo un comando de gato": 1. en realidad no, ten en cuenta las líneas en blanco extra insertadas entre los archivos y 2. Estoy asumiendo (quizás incorrectamente) que este era un script reducido en aras de la simplicidad y la versión real puede tener alguna lógica extra por archivo.) –

3

es una forma demasiado costosa de combinar archivos.

cat ../best-practices/*.textile > ../best_practices.textile 

si desea agregar un espacio en blanco (nueva línea) para cada archivo como se concatena, el uso de awk

awk 'FNR==1{print "">"out.txt"}{print > "out.txt" }' *.textile 

O

awk 'FNR==1{print ""}{print}' file* > out.txt 
+0

d'oh me ganaste por 1 segundo. –

+1

agradable. Gracias. Realmente me encanta Bash conceptualmente. Ahora solo necesito poner mi conocimiento en línea con mi afecto. Saludos –

+0

el título de mis documentos me parece irónico ... heh –

1

Esto le permite intercalar nuevas líneas entre cada entrada archivo como lo ha hecho en su script original:

for f in $FILES; do echo -ne '\n\n' | cat "$f" -; done > $OUTPUT 

Tenga en cuenta que $FILES está desmarcado para que esto funcione (de lo contrario, las nuevas líneas adicionales aparecen una sola vez al final de todos los resultados), pero $f debe citarse para proteger espacios en nombres de archivo, si existen.

40

Como han señalado otros, utilizar cat o awk en lugar de un ciclo de lectura-eco es una forma mucho mejor de hacerlo: evita el problema del recorte del espacio en blanco (y un par de otros con los que no se ha tropezado) , funciona más rápido, y al menos con cat, es simplemente un código más limpio. No obstante, me gustaría intentar conseguir que el ciclo de lectura y eco funcione correctamente.

Primero, el problema de recorte del espacio en blanco: el comando de lectura recorta automáticamente el espacio en blanco inicial y final; esto puede solucionarse cambiando su definición de espacios en blanco al establecer la variable IFS en blanco. Además, read asume que una barra invertida al final de la línea significa que la siguiente línea es una continuación, y debe empalmarse con esta; para arreglar esto, use su indicador -r (raw). El tercer problema aquí es que muchas implementaciones de echo interpretan secuencias de escape en la cadena (por ejemplo, pueden convertir \ n en una nueva línea real); para arreglar esto, use printf en su lugar. Finalmente, al igual que una regla general de higiene de secuencias de comandos, no debe usar cat cuando no lo necesite; use la redirección de entrada en su lugar. Con estos cambios, el bucle interno se ve así:

while IFS='' read -r line; do 
    printf "%s\n" "$line">>$OUTPUT 
done <$f 

... también hay un par de problemas con el guión de influencia: la línea que trata de definir los archivos como la lista de archivos disponibles .textile tiene las citas a su alrededor, lo que significa que nunca se expande a una lista real de archivos.La mejor manera de hacer esto es utilizar una matriz:

FILES=(../best-practices/*.textile) 
... 
for f in "${FILES[@]}" 

(y todas las apariciones de $ f debe ser entre comillas dobles en caso de que cualquiera de los nombres de archivo tienen espacios u otros caracteres extraños en ellos - realmente debería hacer esto con $ OUTPUT también, aunque como eso está definido en el script, es realmente seguro dejarlo).

Finalmente, hay un echo "">$OUTPUT cerca de la parte superior de los bucles sobre archivos que borrará el archivo de salida cada tiempo transcurrido (es decir, al final, solo contiene el último archivo .textile); esto necesita moverse antes del ciclo. No estoy seguro de si la intención aquí fue poner una sola línea en blanco al comienzo del archivo, o tres líneas en blanco entre los archivos (y uno al principio y dos al final), así que no estoy seguro exactamente qué el reemplazo apropiado es De todos modos, esto es lo que puedo con después de fijar todos estos problemas:

#!/bin/sh 
OUTPUT="../best_practices.textile" 
FILES=(../best-practices/*.textile) 

: >"$OUTPUT" 
for f in "${FILES[@]}" 
do 
    echo "Processing $f file..." 
    echo >>"$OUTPUT" 

    while IFS='' read -r line; do 
    printf "%s\n" "$line">>"$OUTPUT" 
    done <"$f" 

    echo >>"$OUTPUT" 
    echo >>"$OUTPUT" 
done 
+0

Gracias por poner el tiempo en este Gordon, fue extremadamente informativo. –

+0

increíble, buscó todo esto, gracias –

+0

La mejor respuesta aquí! Muchas gracias por haberse tomado el tiempo de explicarlo tan claramente. :) – Arnlen

0

La respuesta correcta, la OMI, es this, se reproduce a continuación:

while IFS= read line; do 
    check=${line:0:1} 
done < file.txt 

nota que va a hacerse cargo de situaciones donde la entrada se canaliza desde otro comando, y no solo desde un archivo real.

Tenga en cuenta que también puede simplificar la redirección como se muestra a continuación.

#!/bin/bash 
OUTPUT="../best_practices.textile" 
FILES="../best-practices/*.textile" 
for f in "$FILES" 
do 
    echo "Processing $f file..." 
    { 
    echo 

    while IFS= read line; do 
     echo "$line" 
    done < $f 
    echo 
    echo; 
    } > $OUTPUT 
done 
Cuestiones relacionadas