Сохранение начального пробела при чтении>> построчная запись файла в bash
-
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