読み取り中に先頭の空白を保持する>> bashでファイルを1行ずつ書き込む
-
22-07-2019 - |
質問
テキストファイルのディレクトリをループして、それらを1つのドキュメントに結合しようとしています。これはうまく機能しますが、テキストファイルにはコードスニペットが含まれており、すべての書式設定が左側に折りたたまれています。行の先頭の空白はすべて削除されます。
#!/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 noobですが、高低を検索した後、適切な解決策を見つけることができませんでした。明らかにBASHは一般的に先頭の空白を嫌っています。
解決
代わりに:
cat $f | while read line; do
echo "$line">>$OUTPUT
done
これを実行:
cat $f >>$OUTPUT
(行ごとに物事を行う必要がある場合は、質問にそれを含めるとよいでしょう。)
他のヒント
他の人が指摘したように、read-echoループの代わりにcatまたはawkを使用することは、これを行うためのはるかに優れた方法です-ホワイトスペースのトリミングの問題を回避します(そして、あなたがつまずいたことのない他のいくつか)より高速に実行され、少なくともcatを使用すると、コードが単純になります。それにもかかわらず、read-echoループを正しく動作させるために突き刺したいと思います。
まず、空白のトリミングの問題:readコマンドは、先頭と末尾の空白を自動的にトリミングします。これは、IFS変数を空白に設定して空白の定義を変更することで修正できます。また、readは、行末のバックスラッシュが次の行が継続であることを意味し、この行と一緒につながれることを前提としています。これを修正するには、-r(raw)フラグを使用します。ここでの3番目の問題は、エコーの多くの実装が文字列内のエスケープシーケンスを解釈することです(たとえば、\ 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&quot;&quot;&gt; $ OUTPUT
があり、毎回出力ファイルを消去します(つまり、最後に、最後の.textileファイルのみが含まれます);これはループの前に移動する必要があります。ここでの意図がファイルの先頭に1つの空白行を置くのか、ファイル間に3つの空白行を置くのか(および先頭に1つ、末尾に2つ)あるのかわかりません。適切な代替品です。とにかく、これらの問題をすべて修正した後、次のことができます:
#!/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
が引用符で囲まれていないことに注意してください(それ以外の場合、余分な改行はすべての出力の最後に1回だけ表示されます)。ファイル名にスペースが存在する場合、それを保護します。
正解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