スペースを含む 'for'ループで行全体を読み取る方法


130

forファイルのループを実行しようとしていますが、行全体を表示したいです。しかし、代わりに最後の単語のみを表示します。完全なラインが欲しい。

for j in `cat ./file_wget_med`

do
echo $j

done

実行後の結果:

Found.

私のデータは次のとおりです。

$ cat file_wget_med
2013-09-11 14:27:03 ERROR 404: Not Found.

回答:


179

forスペース、タブ、改行などの空白が見つかると、ループが分割されます。したがって、IFS(Internal Field Separator)を使用する必要があります。

IFS=$'\n'       # make newlines the only separator
for j in $(cat ./file_wget_med)    
do
    echo "$j"
done
# Note: IFS needs to be reset to default!

5
また、IFS = $ "\ n"は "nn"文字列も分割するため、IFSでの一重引用符に注意してください。IFSの設定は、より複雑な構文や関数を使用するよりも信頼性が高いことがわかりました。
erm3nda

23
unset IFS他のコマンドがこの同じシェルで影響を受けないように、後でそれが推奨されるようです。
ボリスデッペン

2
どういう$意味IFS=$'\n'ですか?
レン

2
$文字列内で改行を機能させます。これもlocal IFS=$'\n'関数内で行う必要があります。
アンディレイ

1
それはだ@Renn $'...'"知らANSI-C引用 "
αғsнιη

82

forデフォルトでは、空白(スペース、タブ、改行)でループが分割されます。一度に1 ずつ作業する最も簡単な方法は、while read代わりにループを使用することです。ループは改行で分割されます。

while read i; do echo "$i"; done < ./file_wget_med

私はあなたのコマンドが1行につき1ワードを吐き出すことを期待しています(それは私自身のファイルでテストしたときに起こったことです)。他に何かが起こっている場合、何が原因であるかはわかりません。


8
これはfor i in `ls`; do echo $1; done、whileコマンドに出力をパイプすることによるの優れた代替手段でもありますls|while read i; do echo $i; done。これは、環境変数を変更する必要なく、スペースを含むファイル/フォルダ名で機能します。非常に良い答えです。
-jishi

しばらくは、最初と最後の行を扱う非常に、ではないだけでなく、IFSとのforループしませんでした
grantbow

@jishiは主に表示用に設計されており、ターミナルウィンドウのサイズなどに反応するlsため、|(パイプオペレータ)での使用は安全とは見なされません。findこの目的のためにより安全であることに加えて、より柔軟です。
SeldomNeedy

3
これは素晴らしい答えです。私はそれを使って、Mac OSXで開発していたIOSアプリのリストをPLISTエントリに変換しました。私が使ってclipboadパイピングでファイル入力を置き換えpbpasteを - >pbpaste | while read t; do echo "<string>$t</string>"; done
ビッグリッチ

3
ls -1一緒に使用することができる|ごとに1つの行フォーマットされていない出力を保証するために
AndreyS Scherbakov

19
#!/bin/bash
files=`find <subdir> -name '*'`
while read -r fname; do
    echo $fname
done <<< "$files"

動作確認済みで、おそらく必要なライナーではありませんが、エレガントに行うことはできません。


1
ここにひも!他のすべてのソリューションの中で最もエレガントなIMO
Eliran Malka

5

以下は、ミッチェル・カリーの答えをわずかに拡張したものです。副作用の範囲が狭いため、変数を設定する必要がないため、私はそれが好きです。

#!/bin/bash
while read -r fname; do
    echo $fname
done <<< "`find <subdir>`"

1
あなたは削除することができ-name '*'、それは同じでしょうか?
wjandrea

1

次のように書きます。

cat ./file_wget_med | while read -r j
do
    echo $j
done

元のスクリプトでの変更が最小限で済むため(を使用するソリューションを除きますが、このループ制御ステートメントだけでなく動作もIFS変更しbashます)。


1
catここでパイプとは不要です。whileループはリダイレクションを正常に受け入れます。これが通常、次のように書かれている理由ですwhile IFS= read -r line; do...done < input.txt
Sergiy Kolodyazhnyy

0

マップファイルは、通りのような携帯用ではない、インデックス付きの配列にファイルから行を読み込むための便利な方法です読みが、わずかに速いです。forループを使用すると、サブシェルの作成を回避できます。

#!/bin/bash

mapfile -t < file.txt

for line in "${MAPFILE[@]}"; do
    echo $line
done

パイプラインを使用する場合は、whileループをサブシェルに配置することに留意してください。変数のようなwhileループ内の変更は、スクリプトの外側部分には伝播しません。

例:

#!/bin/bash

a=0
printf %s\\n {0..5} | while read; do
  ((a++))
done
echo $a # 'a' will always be 0.

(より良い解決策):

#!/bin/bash

b=0
while read; do
  ((b++))
done < <(printf %s\\n {0..5})

echo $b # 'b' equal to 6 (works as expected).

-1

Dandalfは機能的な解決策に近づきましたが、未知の量の入力(つまりfind ~/.gdfuse -name '*')の結果を変数に割り当てようとするべきではありません!または、少なくとも配列変数を介してそのようなことをしようとしています。怠け者だと主張するなら!危険な操作なしで行われるダンダルフの解決策は次のとおりです。そして、すべて一行で

while read -r fname; do
  echo $fname;
done <<< `find ~/.gdfuse -name '*'

ちょっと@odoncaoa、二重引用符なしでfindコマンドを実行しようとしましたが、すべての結果が1行として表示されます。「未知の入力量」とこのコマンドと関連する危険性について混乱しています。危険の例と、それらが理論的にどのように発生する可能性があるか教えてください。私は真剣にそれについて怠laになることを望みません、私は他のプログラミング言語でちょうどより快適です。
ダンダルフ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.