スクリプト結果を印刷するときにprintfがIFSを無視するのはなぜですか?

スクリプト結果を印刷するときにprintfがIFSを無視するのはなぜですか?

これはフォローアップです。質問SOに投稿されました。

exceptこのようにして、指定されたファイル名/ディレクトリを除くすべてのファイル名/ディレクトリを印刷する必要があるスクリプトを作成しました。

$ ls
a b c d
$ rm $(except b d)
$ ls
b d

これまでに作成したスクリプトは次のとおりです。

shopt -s extglob
shopt -s nullglob

old_ifs=$IFS
IFS='|'
matches="!($*)"
IFS=' '

printf '%s' $matches
IFS=$old_ifs

これはある程度機能しますが、主な問題はprintfが(空白)IFS設定を尊重しないようです

の間にスペースなしで印刷されます。代わりにechoを使用すると機能します(追加の改行を除く)。しかし、互換性のためにこれを行うにはprintfを使用したいと思います。この目標をどのように達成できますか?私は何が間違っていましたか?

また、修飾子を試してみました%qが、目的の結果は出ませんでした。

printf '%q' $matches

答え1

$ ls
a b c d
$ rm $(except b d)
$ ls
b d

"except"コマンドに渡す前にシェルが出力を分割(および追加の拡張)する方法を決定する方法がないため、これは機能しないか、少なくとも信頼できませんrm

あなたの質問に答えるためにprintfは使用されません$IFS。 IFSを使用する唯一のコマンドはですread。さらに、シェルは接続のトークン化と位置引数に$ IFSを使用します"$*"

次のことができます。

 printf '%s ' $matches

または

 printf '%s\n' $matches

しかし、再び、

 rm $(except a b)

exceptそのコマンドラインを解釈するシェルは独自の状況に応じて出力を分割し$IFS(デフォルトは空白、タブ、または改行であるため、空白以上を使用しても\n違いはありません)、ワイルドカードも拡張します。

aしたがって、and以外のファイルがandの場合は出力bされ"this file.txt"、andを削除しようとします(該当する名前のすべてのファイルに拡張されるため)。**a**except"this file.txt **a** "thisfile.txta**a**a

末尾の空白なしで空白と一致を連結するには、次のようにします。

IFS=
set -- $matches
IFS=' '
matches="$*"

関連情報