Сохранение начального пробела при чтении>> построчная запись файла в bash

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

  •  22-07-2019
  •  | 
  •  

Вопрос

Я пытаюсь перебрать каталог текстовых файлов и объединить их в один документ.Это отлично работает, но текстовые файлы содержат фрагменты кода, и все мое форматирование сворачивается влево.Все начальные пробелы в строке удаляются.

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

Я, по общему признанию, новичок в bash, но после долгих поисков я не смог найти подходящего решения.Очевидно, BASH вообще ненавидит ведущее пустое пространство.

Это было полезно?

Решение

Вместо:

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

Сделайте это:

cat $f >>$OUTPUT

(Если есть причина, по которой вам нужно что-то делать построчно, было бы хорошо включить это в вопрос.)

Другие советы

Как уже отмечали другие, использование cat или awk вместо цикла read-echo - гораздо лучший способ сделать это - избежать проблемы обрезки пробелов (и нескольких других, с которыми вы не сталкивались), работает быстрее, и, по крайней мере, с кошкой, это просто более чистый код. Тем не менее, я хотел бы попытаться заставить цикл чтения-эхо работать правильно.

Во-первых, проблема обрезки пробелов: команда чтения автоматически обрезает начальные и конечные пробелы; это можно исправить, изменив определение пробела, установив пустую переменную IFS. Кроме того, read предполагает, что обратная косая черта в конце строки означает, что следующая строка является продолжением и должна быть соединена вместе с этой; чтобы исправить это, используйте флаг -r (raw). Третья проблема здесь заключается в том, что многие реализации echo интерпретируют escape-последовательности в строке (например, они могут превращать \ n в фактическую новую строку); чтобы исправить это, используйте printf. Наконец, как общее правило гигиены сценариев, вы не должны использовать cat, когда вам это не нужно; вместо этого используйте перенаправление ввода. С этими изменениями внутренний цикл выглядит следующим образом:

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

... есть также несколько других проблем с окружающим скриптом: строка, которая пытается определить FILES как список доступных .textile файлов, имеет кавычки, означающие, что она никогда не будет расширена в фактический список файлов , Лучший способ сделать это - использовать массив:

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

(и все вхождения $ f должны быть в двойных кавычках на тот случай, если в именах файлов есть пробелы или другие забавные символы - действительно следует делать это и с $ OUTPUT, хотя, поскольку это определено в сценарии, это на самом деле безопасно оставить.)

Наконец, есть echo " > $ OUTPUT в верхней части зацикленных файлов, которые будут каждый раз стирать выходной файл (т. е. в конце содержит только последний файл .textile); это должно быть перемещено до цикла. Я не уверен, было ли здесь намерение поставить одну пустую строку в начале файла или три пустые строки между файлами (и одну в начале и две в конце), поэтому я не уверен, что именно соответствующая замена есть. Во всяком случае, вот что я могу сделать после устранения всех этих проблем:

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

это слишком дорогой способ объединения файлов.

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

если вы хотите добавить пробел (перевод строки) к каждому файлу при объединении, используйте awk

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

или

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

Это позволяет перемежать символы новой строки между каждым входным файлом, как вы делали в исходном скрипте:

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

Обратите внимание, что $ FILES не заключено в кавычки, чтобы это работало (в противном случае дополнительные символы новой строки появляются только один раз в конце всех выходных данных), но $ f необходимо заключать в кавычки защитить пробелы в именах файлов, если они существуют.

Правильный ответ, imo, этот , воспроизведенный ниже:

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

Обратите внимание, что он позаботится о ситуациях, когда ввод передается из другой команды, а не только из реального файла.

Обратите внимание, что вы также можете упростить перенаправление, как показано ниже.

#!/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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top