globをlsにパイプ

globをlsにパイプ

文書の内容filelist:

/some/path/*.txt
/other/path/*.dat
/third/path/example.doc

私はこれらのファイルをリストしたいので、次のようにします。

cat filelist | xargs ls

ただし、これらの範囲を拡張する代わりに、次のような結果が得られます。

ls: cannot access '/some/path/*.txt': No such file or directory  
ls: cannot access '/other/path/*.dat': No such file or directory  
/third/path/example.doc

答え1

シェルは球を拡張します。ここでは非常にまれです。 Bourneに似たシェル(zshを除く)では、引用符で囲まれていないコマンドの置き換え時に暗黙の分割+glob演算子を呼び出すと便利です。

IFS='
' # split on newline only
set +o noglob # make sure globbing is not disabled
ls -ld -- $(cat filelist) # split+glob

では、zsh次の操作を行います。

ls -ld -- ${(f)~"$(<filelist)"}

f改行文字に分割されたパラメータ拡張フラグはどこにありますか?~ 必要パラメータの拡張やコマンドの置き換え時には、デフォルトでワイルドカードは実行されません。

一致するファイルのリストが大きい場合、次の問題が発生する可能性があります。パラメータリストが長すぎます。バグ(ほとんどのシステムでのシステムコールの制限execve())、xargsそれ以外の場合は解決することができます。では、zsh以下を使用できますzargs

autoload zargs
zargs --eof= -- ${(f)~"$(<filelist)"} '' ls -ld --

必要に応じて制限を避けるために、リストが分割されて複数回実行されますzargslsxargs

あるいは、リストを組み込みコマンドに渡すこともできます(したがってシステムexecve()コールは不要です)。

ファイルリストのみ印刷:

print -rC1 -- ${(f)~"$(<filelist)"}

またはxargsNULで区切って入力してください。

print -rNC1 -- ${(f)~"$(<filelist)"} |
  xargs -r0 ls -ld --

ファイルと一致しない glob がある場合は、zshエラーメッセージが表示されます。これらのglobを何も拡張したくない場合は、Nglob修飾子をglobに追加できます(これはnullglobglobごとに有効になります)。

print -rNC1 -- ${(f)^~"$(<filelist)"}(N) |
  xargs -r0 ls -ld --

これを追加すると、glob演算子を持たないすべての行がglobに変換されるため、パスで参照されているファイルと存在しないファイルがフィルタリングされます。つまり、Express asを渡してこのオプションを有効にしないと、(N)glob修飾子は使用できません。 。また、修飾子は任意のコードを実行できるため、ファイルの内容が信頼できるソースから出ることが重要です。filelist(#q...)extendedglobfilelist

Glob を含む他の Bourne 様シェルでは、bash一致しない glob は変更されずに残るので、文字通りlsそのファイルが存在しないと報告できるエラーとして渡されます。

では、bashこのオプション(zshからコピー)を使用しnullglobて、具体的に一致するglobがない場合を処理できます。

shopt -s nullglob
IFS=$'\n'
set +o noglob
set -- $(<filelist)
(( $# == 0 )) || printf '%s\0' "$@" | xargs -r0 ls -ld --

bashzsh、glob修飾子と同等のものはありません。 glob演算子のない行(yoursなど/third/path/example.doc)がglobとして扱われ、実際のファイルと一致しない場合に削除するようにするには、@()これらの行に追加できます(必須extglob)。ただし、これは文字で終わる行では機能しません/。ただし、@()最後の非文字に追加し、常に存在するという/事実に頼ることができます。/

shopt -s nullglob extglob
IFS=$'\n'
set +o noglob
set -- $(LC_ALL=C sed 's|.*[^/]|&@()|' filelist)
(( $# == 0 )) || printf '%s\0' "$@" | xargs -r0 ls -ld --

それにもかかわらず、サポートされるglob演算子のリストはシェルによって大きく異なります。しかし、あなたの例()で使用されている唯一のものは*誰でもサポートする必要があります。

答え2

スクリプトを少し変更し、sh次から呼び出すことができますxargs

cat filelist | xargs -i -- /bin/sh -c 'ls $1' _X_ {}

xargsそれともファイル自体を読んでみましょう。

xargs -a filelist -i -- /bin/sh -c 'ls $1' _X_ {}

答え3

while read filepattern
do
    ls $filepattern
done < filelist

この場合、ファイルグロービングを介して変数値を拡張したいので、引用符なしで変数を使用する必要があります。

/some/path/*.txtシェルは一致するファイルのリストに置き換えられます。

不要なファイル名のみをリストしたい場合lsecho次のものを使用できますecho $filepattern

上記のコードとは異なり、引用符を使用すると、質問の例と同じエラーが発生します。なぜなら、シェルは/some/path/*.txtto のように変更されていない文字列を渡すからですls

while read filepattern
do
    ls "$filepattern"
done < filelist

関連情報