web-dev-qa-db-ja.com

ファイルの末尾に改行がない場合に「while read」(Bash)を使用してファイルの最後の行を読み取る方法

次の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ループとまったく同じように、つまり入力ファイルの場所をハードコーディングせずに動作させるにはどうすればよいですか?

48
Mathias Bynens

最初の例では、stdinから読み込んでいると仮定しています。 2番目のコードブロックで同じことを行うには、リダイレクトを削除して$ REPLYをエコーするだけです。

DONE=false
until $DONE ;do
read || DONE=true
echo $REPLY
done
14
netcoder

次の構成を使用します。

while IFS= read -r LINE || [[ -n "$LINE" ]]; do
    echo "$LINE"
done

入力のヌル文字以外のほとんどすべてで動作します:

  • 空白行で開始または終了するファイル
  • 空白で始まるまたは終わる行
  • 終了改行を持たないファイル
37
Adam Bryzak

Whileループでgrepを使用する:

while IFS= read -r line; do
  echo "$line"
done < <(grep "" file)

grep .の代わりにgrep ""を使用すると、空の行がスキップされます。

注意:

  1. IFS=を使用すると、行のインデントはそのままになります。

  2. ほとんどの場合、-rオプションをreadとともに使用する必要があります

  3. 最後に改行のないファイルは、標準のUNIXテキストファイルではありません。

5
Jahid

readの代わりに、GNU Coreutilsのようなteecatなどを使用してください。

標準入力から

readvalue=$(tee)
echo $readvalue

ファイルから

readvalue=$(cat filename)
echo $readvalue
2
prabhakaran9397

ここでの基本的な問題は、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

覚えておいてください:

  • NUL文字は無視されます
  • echoの動作を模倣するためにcatを使用することに固執する場合は、echo -n upon EOF detected(条件[ "$eof" == true ]
1
vaab

これは私が使用しているパターンです:

while read -r; do
  echo "${REPLY}"
done
[[ ${REPLY} ]] && echo "${REPLY}"

これは、whileループからでも、readからの「テスト」がゼロ以外のコードで終了し、readが組み込み変数$REPLY(またはreadで割り当てることを選択した変数)。

1
Olli K

@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はパイプを有効にし、そうでなければ引数を変換するだけです)

0
unsynchronized