例:
% touch -- safe-name -name-with-dash-prefix "name with space" \
'name-with-double-quote"' "name-with-single-quote'" \
'name-with-backslash\'
xargs
は二重引用符を処理できないようです:
% ls | xargs ls -l
xargs: unmatched double quote; by default quotes are special to xargs unless you use the -0 option
ls: invalid option -- 'e'
Try 'ls --help' for more information.
-0
オプション、ダッシュプレフィックスが付いた名前に問題があります。
% ls -- * | xargs -0 -- ls -l --
ls: invalid option -- 'e'
Try 'ls --help' for more information.
これは、改行、制御文字などの他の潜在的に問題のある文字を使用する前です。
POSIX仕様 は、その例を示しています。
ls | sed -e 's/"/"\\""/g' -e 's/.*/"&"/' | xargs -E '' printf '<%s>\n'
(ファイル名はbytesの任意のシーケンスです(/
およびNULL以外)およびsed
/xargs
textが必要ですが、ロケールをCに修正して(NUL以外のすべてのバイトが有効な文字になる場合)、信頼性を高める必要があります(引数の最大長に非常に低い制限があるxargs
実装を除く))
-E ''
は、一部のxargs
実装で必要です。この実装がない場合、_
引数を理解して入力の終わりを示します(echo a _ b | xargs
はa
を出力します(例のみ)。
GNU xargs
では、以下を使用できます。
ls | xargs -d '\n' printf '<%s>\n'
GNU xargs
には、他のいくつかの実装によってコピーされた-0
もあるので、
ls | tr '\n' '\0' | xargs -0 printf '<%s>\n'
少しポータブルです。
これらはすべて、ファイル名に改行文字が含まれていないことを前提としています。改行文字を含むファイル名がある場合、ls
の出力は後処理できません。あなたが取得する場合:
a
b
これは、2つのa
ファイルとb
ファイル、またはa<newline>b
という名前のファイルのいずれかである可能性があり、知る方法はありません。
GNU ls
には--quoting-style=Shell-always
があり、出力を明確にして後処理できる可能性がありますが、引用はxargs
が期待する引用と互換性がありません。 xargs
は、引用の"..."
、\x
、'...'
形式を認識します。ただし、"..."
と'...'
はどちらも強い引用符であり、改行文字を含めることはできません(\
のみがxargs
の改行文字をエスケープできます)。そのため、shの引用と互換性がありません。ここで、'...'
だけが強い引用符です(改行文字を含めることができます)が、\<newline>
は、エスケープされた改行ではなく、行連結(削除されます)です。
シェルを使用してその出力を解析し、xargs
で予期される形式で出力することができます。
eval "files=($(ls --quoting-style=Shell-always))"
[ "${#files[#]}" -eq 0 ] || printf '%s\0' "${files[@]}" |
xargs -0 printf '<%s>\n'
xargs
が-0
のnull区切りの入力オプションを理解するには、送信側も送信するデータにnull区切り文字を適用する必要があります。
それ以外の場合、2つの間に同期はありません。
1つのオプションは、そのような区切り文字を配置できるGNU find
コマンドです。
find . -maxdepth 1 ! -name . -print0 | xargs -0 ls -ld
あなたが言ったように、xargs
は-0
を使用しない限り、一致しない二重引用符を好まないが、-0
はnullで終了するデータをフィードする場合にのみ意味があります。したがって、これは失敗します:
$ echo * | xargs
xargs: unmatched double quote; by default quotes are special to xargs unless you use the -0 option
name-with-backslash -name-with-dash-prefix
しかし、これはうまくいきます:
$ printf '%s\0' -- * | xargs -0
-- name-with-backslash\ -name-with-dash-prefix name-with-double-quote" name-with-single-quote' name with space safe-name
いずれにしても、あなたの基本的なアプローチはこれを行うための本当に最良の方法ではありません。 xargs
とls
をいじるのではなく、代わりにシェルグロブを使用します。
$ for f in *; do ls -l -- "$f"; done
-rw-r--r-- 1 terdon terdon 4142 Aug 11 16:03 a
-rw-r--r-- 1 terdon terdon 0 Aug 11 15:34 'name-with-backslash\'
-rw-r--r-- 1 terdon terdon 0 Aug 11 15:34 -name-with-dash-prefix
-rw-r--r-- 1 terdon terdon 0 Aug 11 15:34 'name-with-double-quote"'
-rw-r--r-- 1 terdon terdon 0 Aug 11 15:34 "name-with-single-quote'"
-rw-r--r-- 1 terdon terdon 0 Aug 11 15:34 'name with space'
-rw-r--r-- 1 terdon terdon 0 Aug 11 15:34 safe-name
コマンドの出力を解析するls
つまり 解析するように設計されていないis複数の文字を処理するように設計されていません (例:new lines
および{}
)シェル自体がそれを行う場合:
set -- *; for f; do echo "<$f>"; done
set -- *
for f
do ls "$f"
done
または、1つのコマンドラインで:
$ set -- *; for f; do echo "<$f>"; done
<name-with-backslash\>
<-name-with-dash-prefix>
<name-with-double-quote">
<name-with-single-quote'>
<name with space>
<safe-name>
<with_a
newline>
出力では改行が完全に問題なく処理されます(最後のファイル名としてnの例があります)。
または、ファイルの数によってシェルが遅くなる場合は、findを使用します。
$ find ./ -type f -exec echo '<{}>' \;
<./safe-name>
<./with_a
newline>
<./name-with-double-quote">
<./-name-with-dash-prefix>
<./name with space>
<./name-with-single-quote'>
<./name-with-backslash\>
Findは、シェルとは異なり、すべてのドットファイルとすべてのサブディレクトリを処理することに注意してください。