次の.txtファイルがあります。
Marco
Paolo
Antonio
行ごとに読みたいので、各行に.txt行の値を変数に割り当てます。私の変数が$name
であるとすると、フローは次のとおりです。
- ファイルから最初の行を読み取る
- 割り当て
$name
= "マルコ" - いくつかのタスクを行う
$name
- ファイルから2行目を読み取る
- 割り当て
$name
= "Paolo"
次の.txtファイルがあります。
Marco
Paolo
Antonio
行ごとに読みたいので、各行に.txt行の値を変数に割り当てます。私の変数が$name
であるとすると、フローは次のとおりです。
$name
= "マルコ"$name
$name
= "Paolo"回答:
次の例では、引数として渡されたファイルを1行ずつ読み取ります。
while IFS= read -r line; do
echo "Text read from file: $line"
done < my_filename.txt
これは、ループ内のファイルから行を読み取るための標準形式です。説明:
IFS=
(またはIFS=''
)先頭/末尾の空白が削除されないようにします。-r
バックスラッシュエスケープが解釈されないようにします。または、bashファイルのヘルパースクリプトに、例の内容を入れます。
#!/bin/bash
while IFS= read -r line; do
echo "Text read from file: $line"
done < "$1"
上記をfilename readfile
でスクリプトに保存すると、次のように実行できます。
chmod +x readfile
./readfile filename.txt
ファイルが標準のPOSIXテキストファイルでない場合(=改行文字で終了していない場合)、ループを変更して、後続の部分的な行を処理できます。
while IFS= read -r line || [[ -n "$line" ]]; do
echo "Text read from file: $line"
done < "$1"
ここで|| [[ -n $line ]]
は、最後の行がaで終わっていない場合に無視されないようにします\n
(read
EOFが検出されると、ゼロ以外の終了コードが返されるため)。
ループ内のコマンドも標準入力から読み取る場合、によって使用されるファイル記述子は、read
他の何かに出くわす可能性があります(標準ファイル記述子を避けます)。たとえば、
while IFS= read -r -u3 line; do
echo "Text read from file: $line"
done 3< "$1"
(非Bashシェルは知らないかもしれませんread -u3
; read <&3
代わりに使用してください。)
ssh
、-n
フラグを指定しないと、効果的にループを脱出できます。これには十分な理由があると思いますが、コードが失敗する原因を特定するのにしばらく時間がかかりました。
ffmpeg
消費stdin が原因です。ラインに追加</dev/null
するffmpeg
と、ループができなくなります。またはループに代替FDを使用します。その「代替FD」アプローチは次のようになりwhile IFS='' read -r line <&3 || [[ -n "$line" ]]; do ...; done 3<"$1"
ます。
.sh
拡張機能についてアドバイスします。UNIXの実行可能ファイルには通常、拡張機能はありません(実行しませんls.elf
)。また、bashシバン(およびなどのbash専用ツール[[ ]]
)とPOSIX sh互換性を意味する拡張機能は、内部的に矛盾しています。
以下を表す-r
フラグを使用することをお勧めしますread
。
-r Do not treat a backslash character in any special way. Consider each
backslash to be part of the input line.
から引用していman 1 read
ます。
もう1つは、ファイル名を引数として受け取ることです。
ここに更新されたコードがあります:
#!/usr/bin/bash
filename="$1"
while read -r line; do
name="$line"
echo "Name read from file - $name"
done < "$filename"
sh
ではなかったと疑っていますbash
。|| [[ -n "$line" ]]
受け入れられた回答の構文で使用されている拡張テストコマンドはバシズムです。とは言っても、その構文には実際に適切な意味があります。これにより、改行がなくても入力ファイルの最後の行までループが継続されます。これをPOSIX準拠の方法で実行したい場合は|| [ -n "$line" ]
、[
ではなくを使用し[[
ます。
IFS=
ためにread
空白をトリミング防ぐために。
次のBashテンプレートを使用すると、ファイルから一度に1つの値を読み取って処理できるはずです。
while read name; do
# Do what you want to $name
done < filename
*
。
#! /bin/bash
cat filename | while read LINE; do
echo $LINE
done
Enter
、最後の行の後に押す必要があります)。そうでない場合、最後の行は無視されます。少なくともそれが私に起こったことです。
多くの人々が過度に最適化されたソリューションを投稿しています。私はそれが間違っているとは思いませんが、これがどのように機能しているかを誰もが簡単に理解できるようにするには、あまり最適化されていないソリューションが望ましいと思います。これが私の提案です:
#!/bin/bash
#
# This program reads lines from a file.
#
end_of_file=0
while [[ $end_of_file == 0 ]]; do
read -r line
# the last exit status is the
# flag of the end of file
end_of_file=$?
echo $line
done < "$1"
使用する:
filename=$1
IFS=$'\n'
for next in `cat $filename`; do
echo "$next read from $filename"
done
exit 0
設定がIFS
異なると、奇妙な結果になります。
*
行に1つを含むファイルで試しましたか?とにかく、これはアンチパターンです。forで行を読み取らないでください。
read
アプローチは、コミュニティコンセンサスによるベストプラクティスアプローチです。コメントで言及した警告は、ループがffmpeg
stdinから読み取るコマンド(など)を実行するときに適用されるものであり、ループに非stdin FDを使用するか、そのようなコマンドの入力をリダイレクトすることで簡単に解決されます。対照的に、for
-loopアプローチのグロビングバグを回避することは、シェルグローバル設定の変更を行う(そして元に戻す必要がある)ことを意味します。
for
あなたは、あなたがた場合でもデータのギガバイトを超えるループしている場合、それは完全に使用できなくなり、ここですべてのコンテンツがループを全く実行を開始する前に読まれなければならないことを意味を使用したループアプローチ持ち無効をグロビング; while read
ループは、サブプロセスの生成コンテンツがまだ実行されている間(このような目的をストリーミングするために使用可能である)の実行を開始できることを意味し、現時点では複数の単一ラインのデータよりも記憶する必要がなく、また、制限されたメモリの消費量を有しています。
while
ベースのアプローチでも*文字の問題があるようです。上記の受け入れられた回答のコメントを参照してください。ただし、アンチパターンであるファイルの反復については議論しません。
入力ファイルとユーザー入力の両方(またはstdinからの何か)を処理する必要がある場合は、次のソリューションを使用します。
#!/bin/bash
exec 3<"$1"
while IFS='' read -r -u 3 line || [[ -n "$line" ]]; do
read -p "> $line (Press Enter to continue)"
done
受け入れられた回答とbash-hackersリダイレクトチュートリアルに基づいています。
ここでは、スクリプト引数として渡されたファイルのファイル記述子3を開き、read
この記述子を入力として使用するように指示します(-u 3
)。したがって、ユーザー入力を読み取れるように、端末または別の入力ソースに接続されたデフォルトの入力記述子(0)をそのままにします。
適切なエラー処理:
#!/bin/bash
set -Ee
trap "echo error" EXIT
test -e ${FILENAME} || exit
while read -r line
do
echo ${line}
done < ${FILENAME}
以下はファイルの内容を出力するだけです:
cat $Path/FileName.txt
while read line;
do
echo $line
done
bashでIFS(内部フィールドセパレーター)ツールを使用し、行をトークンに分割するために使用する文字を定義します。デフォルトでは、< tab > / < space > / < newLine >が含まれます
ステップ1:ファイルデータを読み込み、リストに挿入します。
# declaring array list and index iterator
declare -a array=()
i=0
# reading file in row mode, insert each line into array
while IFS= read -r line; do
array[i]=$line
let "i++"
# reading from file path
done < "<yourFullFilePath>"
ステップ2:出力を反復して出力します。
for line in "${array[@]}"
do
echo "$line"
done
配列内の特定のインデックスをエコー:配列内の変数へのアクセス:
echo "${array[0]}"