分割時にファイルからIFS値が削除されるのはなぜですか?

分割時にファイルからIFS値が削除されるのはなぜですか?
IFS=$'?\n'
for line in $(cat "./newfiletoread")
do printf "${line}\n"
done

ファイルの内容は次のとおりです。こんにちは!大丈夫ですか?人生はどうですか?私の人生は冬の朝ほど退屈です!

上記のコードは「?」または '!' が見つかったら、ファイルの内容を分割します。 'または'/n'なら大丈夫です。ただし、拡張中にシェルはファイルからこれらの文字を削除します。以下は私が得た結果です。

Hello there 
How are you doing
How is life
Mine is as boring as a winter morning

コマンドが実行される前にこれらのIFS値を空白に置き換えると、シェルが機能することがわかります。分割中にこれらの区切り文字を保持する方法はありますか?以下のような出力を得たいです。

Hello there! 
How are you doing?
How is life?
Mine is as boring as a winter morning!

答え1

まず、ファイルから行を読まないでくださいfor

文字列分割についてどこかで読みました。split何を捨てるべきかを知るときに使用し、何を維持するべきかを知るときに正規表現を使用します。またはそのようなもの。

シェル単語分割を使用する際の問題$IFSは、その変数のすべての文字が分割に使用され、どの文字かわからないことです。

Bashでは、次のように書くことができます。

line='Hello there! How are you doing? How is life? Mine is as boring as a winter morning!'
line=${line//\?/$'"?\n'}
line=${line//\!/$'"!\n'}
echo "$line"
Hello there"!
 How are you doing"?
 How is life"?
 Mine is as boring as a winter morning"!

先行スペースに注意してください。これはより複雑なパターンで解決できます。line=${line//\?*([[:blank:]])/$'"?\n'}

私は以下を使用しますsed

line='Hello there! How are you doing? How is life? Mine is as boring as a winter morning!'
new=$( sed 's/[?!][[:blank:]]*/&\n/g' <<<"$line" )
echo "$new"
Hello there! 
How are you doing? 
How is life? 
Mine is as boring as a winter morning!

split()区切り文字をキャプチャする機能がありますが、それを使用するのは非常に冗長です。

echo "$line" | awk '{
    n = split($0, words, /[!?][[:blank:]]*/, seps)
    for (i = 1; i < n; i++) 
        print words[i] seps[i]
    print words[n]}
'

関連情報