Preservar los espacios en blanco iniciales mientras lee > > escribir un archivo línea por línea en bash

StackOverflow https://stackoverflow.com/questions/1648055

  •  22-07-2019
  •  | 
  •  

Pregunta

Estoy intentando recorrer un directorio de archivos de texto y combinarlos en un documento. Esto funciona muy bien, pero los archivos de texto contienen fragmentos de código, y todo mi formato se contrae a la izquierda. Se eliminan todos los espacios en blanco iniciales en una línea.

#!/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

Ciertamente soy un novato bash, pero después de buscar por todas partes no pude encontrar una solución adecuada. Aparentemente, BASH odia el espacio en blanco líder en general.

¿Fue útil?

Solución

En lugar de:

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

Haz esto:

cat $f >>$OUTPUT

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

Otros consejos

Como otros han señalado, usar cat o awk en lugar de un ciclo de lectura-eco es una forma mucho mejor de hacer esto: evita el problema de recorte de espacios en blanco (y un par de otros con los que no te has topado), corre más rápido, y al menos con cat, es simplemente un código más limpio. Sin embargo, me gustaría intentar que el ciclo de lectura-eco funcione correctamente.

Primero, el problema de recorte de espacios en blanco: el comando de lectura recorta automáticamente los espacios en blanco iniciales y finales; esto se puede solucionar cambiando su definición de espacio en blanco estableciendo la variable IFS en blanco. Además, leer supone que una barra invertida al final de la línea significa que la siguiente línea es una continuación, y se debe unir con esta; Para solucionar esto, use su bandera -r (raw). El tercer problema aquí es que muchas implementaciones de eco 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, solo como una regla general de higiene de los scripts, no debe usar cat cuando realmente no lo necesita; use la redirección de entrada en su lugar. Con esos 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 otros problemas con el script circundante: la línea que intenta definir ARCHIVOS como la lista de archivos .textile disponibles tiene comillas, lo que significa que nunca se expande en una lista real de archivos . La mejor manera de hacer esto es usar una matriz:

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

(y todas las apariciones de $ f deben estar entre comillas dobles en caso de que alguno de los nombres de archivo tenga espacios u otros caracteres divertidos), también debería hacer esto con $ OUTPUT, aunque ya que está definido en el script, es realmente seguro de dejar).

Finalmente, hay un echo " " > $ OUTPUT cerca de la parte superior de los archivos de bucle que borrará el archivo de salida cada vez (es decir, al final, solo contiene el último archivo .textile); esto necesita ser movido antes del ciclo. No estoy seguro de si la intención aquí era poner una sola línea en blanco al comienzo del archivo, o tres líneas en blanco entre los archivos (y una al principio y dos al final), por lo que no estoy seguro exactamente qué El reemplazo apropiado es. De todos modos, esto es lo que puedo hacer después de solucionar 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

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) a cada archivo a medida que concatena, use awk

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

O

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

Esto le permite intercalar nuevas líneas entre cada archivo de entrada como lo hizo en su secuencia de comandos original:

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

Tenga en cuenta que $ FILES no está entrecomillado para que esto funcione (de lo contrario, las nuevas líneas adicionales aparecen solo una vez al final de toda la salida), pero se debe citar $ f para proteger espacios en los nombres de archivo, si existen.

La respuesta correcta, imo, es esto , reproducido a continuación:

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

Tenga en cuenta que se ocupará de las situaciones en las que 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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top