次のBashスクリプトがあるとします。
while read SCRIPT_SOURCE_LINE; do
echo "$SCRIPT_SOURCE_LINE"
done
最後に改行がないファイルの場合、これは最終行を事実上スキップすることに気付きました。
私は周りの解決策を探しました そしてこれを見つけました :
読み取りが行末ではなくファイルの終わりに達すると、データを読み取って変数に割り当てますが、ゼロ以外のステータスで終了します。ループが構築されている場合は、「読みながら;何かを行う;完了
そのため、読み取り終了ステータスを直接テストする代わりに、フラグをテストし、ループ本体内から読み取りコマンドにそのフラグを設定させます。読み取りは、読み取り終了ステータスに関係なく、ループ本体全体が実行されます。これは、読み取りがループ内のコマンドのリストの1つにすぎず、ループがまったく実行されるかどうかを決定する要素ではないためです。
DONE=false until $DONE ;do read || DONE=true # process $REPLY here done < /path/to/file.in
このソリューションを書き換えて、以前のwhile
ループとまったく同じように、つまり入力ファイルの場所をハードコーディングせずに動作させるにはどうすればよいですか?
最初の例では、stdinから読み込んでいると仮定しています。 2番目のコードブロックで同じことを行うには、リダイレクトを削除して$ REPLYをエコーするだけです。
DONE=false
until $DONE ;do
read || DONE=true
echo $REPLY
done
次の構成を使用します。
while IFS= read -r LINE || [[ -n "$LINE" ]]; do
echo "$LINE"
done
入力のヌル文字以外のほとんどすべてで動作します:
Whileループでgrep
を使用する:
while IFS= read -r line; do
echo "$line"
done < <(grep "" file)
grep .
の代わりにgrep ""
を使用すると、空の行がスキップされます。
注意:
IFS=
を使用すると、行のインデントはそのままになります。
最後に改行のないファイルは、標準のUNIXテキストファイルではありません。
readの代わりに、GNU Coreutilsのようなtee、catなどを使用してください。
標準入力から
readvalue=$(tee)
echo $readvalue
ファイルから
readvalue=$(cat filename)
echo $readvalue
ここでの基本的な問題は、read
がEOFに遭遇すると、たとえ変数を正しく供給しても、errorlevel 1を返すということです。
したがって、ループ内でerrorlevel read
をすぐに使用できます。そうしないと、最後のデータは解析されません。しかし、あなたはこれを行うことができます:
eof=
while [ -z "$eof" ]; do
read SCRIPT_SOURCE_LINE || eof=true ## detect eof, but have a last round
echo "$SCRIPT_SOURCE_LINE"
done
行を解析するための非常に堅実な方法が必要な場合は、以下を使用する必要があります。
IFS='' read -r LINE
覚えておいてください:
echo
の動作を模倣するためにcat
を使用することに固執する場合は、echo -n
upon EOF detected(条件[ "$eof" == true ]
)これは私が使用しているパターンです:
while read -r; do
echo "${REPLY}"
done
[[ ${REPLY} ]] && echo "${REPLY}"
これは、while
ループからでも、read
からの「テスト」がゼロ以外のコードで終了し、read
が組み込み変数$REPLY
(またはread
で割り当てることを選択した変数)。
@Netcoderの答えは良いです。この最適化により、空の空白行が削除され、元の行のように最後の行に改行が含まれないようになります。
DONE=false
NL=
until $DONE ;do
if ! read ; then DONE=true ; NL='-n ';fi
echo $NL$REPLY
done
このバリエーションを使用して、grepを満足させるために「[」を含むテキストのパイピングを許可する2つの関数を作成しました。 (他の翻訳を追加できます)
function grepfix(){
local x="$@";
if [[ "$x" == '-' ]]; then
local DONE=false
local xx=
until $DONE ;do
if ! IFS= read ; then DONE=true ; xx="-n "; fi
echo ${xx}${REPLY//\[/\\\[}
done
else
echo "${x//\[/\\\[}"
fi
}
function grepunfix(){
local x="$@";
if [[ "$x" == '-' ]]; then
local DONE=false
local xx=
until $DONE ;do
if ! IFS= read ; then DONE=true ; xx="-n "; fi
echo ${xx}${REPLY//\\\[/\[}
done
else
echo "${x//\\\[/\[}"
fi
}
(渡す-$ 1はパイプを有効にし、そうでなければ引数を変換するだけです)