xargsからパイプ出力を正しくエスケープします。

xargsからパイプ出力を正しくエスケープします。

例:

% 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.

改行文字、制御文字など、潜在的に問題のある他の文字を使用する前です。

答え1

これPOSIX仕様あなたに例を与える:

ls | sed -e 's/"/"\\""/g' -e 's/.*/"&"/' | xargs -E '' printf '<%s>\n'

(ファイル名はランダムな順序です。バイト( /NULLを除く)およびsed/xargs期待テキスト、信頼できるようにロケールをC(NUL以外のすべてのバイトが有効な文字を生成する場合)に変更する必要があります(xargs引数の最大長の制限が非常に低い実装を除く)。

-E ''一部の実装では、入力の終わり(出力のみ)を表す引数をxargs理解する必要があります。_echo a _ b | xargsa

GNUでは、xargs次のことができます。

ls | xargs -rd '\n' printf '<%s>\n'

(また、追加された-r(またGNU拡張)は、入力が空の場合、コマンドは実行されないことです。)

GNUには他の実装によってコピーされたxargsものもあるので、次のようになります。-0

ls | tr '\n' '\0' | xargs -0 printf '<%s>\n'

携帯性が少し良くなりました。

これらすべては、ファイル名に改行文字が含まれていないと仮定します。ファイル名に改行がある場合、出力はlsまったく後処理されません。あなたが得る場合:

a
b

これはaandbファイルでも、名前というファイルでもかまいませんa<newline>b。知る方法はありません。

GNUには出力を明確にし、後処理するls機能がありますが、引用は予想される引用と互換性がありません。識別と引用フォーム。ただし、andは両方とも強い引用符であり、改行文字を含めることはできません(改行文字のみエスケープできます)。したがって、強い引用符のみがあり、改行文字を含めることができますが、エスケープの代わりに行の連続(削除)を持つs​​h引用符と互換性がありません。改行文字。--quoting-style=shell-alwaysxargsxargs"..."\x'...'"..."'...'\xargs'...'\<newline>

シェルを使用してこの出力を解析し、予想される形式で出力できますxargs

eval "files=($(ls --quoting-style=shell-always))"
[ "${#files[@]}" -eq 0 ] || printf '%s\0' "${files[@]}" |
  xargs -0 printf '<%s>\n'

あるいは、シェルにファイルのリストをインポートし、NULで区切って渡すこともできますxargs。たとえば、次のようになります。

  • そしてzsh

    print -rNC1 -- *(N) | xargs -r0 printf '<%s>\n'
    
  • そしてksh93

    (set -- ~(N)*; (($# == 0)) || printf '%s\0' "$@") |
      xargs -r0 printf '<%s>\n'
    
  • そしてfish

    begin set -l files *; string join0 -- $files; end |
      xargs -r0 printf '<%s>\n'
    
  • そしてbash

    (
      shopt -s nullglob
      set -- *
      (($# == 0)) || printf '%s\0' "$@"
    ) | xargs -r0 printf '<%s>\n'
    

2023年編集。 GNU coreutilsバージョン9.0(2021年9月)以降、GNUにはls--zeroのコマンドで使用できるオプションがありますxargs -r0

ls --zero | xargs -r0 printf '<%s>\n'

答え2

xargsnull で区切られた入力オプションを理解するには、-0送信者は送信データに null 区切り文字も適用する必要があります。

それ以外の場合、2 つの間で同期は発生しません。

1つのオプションは、GNU findこれらの区切り文字を配置できるコマンドです。

find . -maxdepth 1 ! -name . -print0 | xargs -0 ls -ld

答え3

あなたが言ったようにを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

とにかく、基本的なアプローチは実際にこれを行う最善の方法ではありません。xargsand と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

答え4

これを試すのはとても愚かなことですコマンドの出力を解析します。lsそれは解析用に設計されていない命令を出す複数の文字を処理するのに適していません(例:new linesおよび{})シェルがそれ自体でこれを行う場合:

set -- *; for f; do echo "<$f>"; done

set    -- *
for    f
do     ls "$f"
done

または、コマンドラインで次の操作を行います。

$ 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はシェルとは異なり、すべてのドットファイルとすべてのサブディレクトリを処理します。

関連情報