web-dev-qa-db-ja.com

xargsにスペースを含むファイル名を処理させる

$ ls *mp3 | xargs mplayer  

Playing Lemon.  
File not found: 'Lemon'  
Playing Tree.mp3.  
File not found: 'Tree.mp3'  

Exiting... (End of file)  

ファイル "Lemon Tree.mp3"にスペースが含まれているため、私のコマンドは失敗します。したがって、xargsは2つのファイルであると見なします。このようなファイル名でfind + xargsを機能させることはできますか?

212

xargsコマンドは、区切り文字として空白文字(タブ、スペース、改行)を使用します。次のような-dオプションを使用して、改行文字( '\ n')のみを絞り込むことができます。

ls *.mp3 | xargs -d '\n' mplayer

GNU xargsでのみ機能します。 BSDシステムの場合、次のように-0オプションを使用します。

ls *.mp3 | xargs -0 mplayer

このメソッドはより単純で、GNU xargsでも機能します。

190
Ray

Xargsユーティリティは、スペース、タブ、改行、およびファイルの終わりで区切られた文字列を標準入力から読み取り、文字列を引数としてユーティリティを実行します。

スペースを区切り文字として使用しないでください。これは、xargsの区切り文字を変更することで実行できます。マニュアルによると:

 -0      Change xargs to expect NUL (``\0'') characters as separators,
         instead of spaces and newlines.  This is expected to be used in
         concert with the -print0 function in find(1).

といった:

 find . -name "*.mp3" -print0 | xargs -0 mplayer

7番目のmp3の再生に関する質問に答えるため。実行する方が簡単です

 mplayer "$(ls *.mp3 | sed -n 7p)"
197
Jens

試して

find . -name \*.mp3 -print0 | xargs -0 mplayer

の代わりに

ls | grep mp3 
24
Scott C Wilson

macOSのxargsには-dオプションがないため、このソリューションでは代わりに-0を使用します。

Lsを取得して1行に1つのファイルを出力し、改行をヌルに変換し、デリミタとしてヌルを使用するようにxargsに指示します。

ls -1 *mp3 | tr "\n" "\0" | xargs -0 mplayer

9
Graeme Pyle

Dick.Guertinの回答[1]は、ファイル名のスペースをエスケープできることが、ここで提案されている他の解決策(空白文字ではなくヌル文字をセパレーターとして使用するなど)に代わる価値があることを示唆しました。しかし、それはもっと簡単かもしれません-あなたは本当にユニークなキャラクターを必要としません。エスケープされたスペースを直接sedに追加することができます:

ls | grep ' ' | sed 's| |\\ |g' | xargs ...

さらに、grepが必要なのは、名前にスペースを含むファイルが必要な場合onlyのみです。より一般的には(たとえば、一部にはスペースがあり、一部にはスペースがないファイルのバッチを処理する場合)、grepをスキップします。

ls | sed 's| |\\ |g' | xargs ...

そして、もちろん、ファイル名には空白以外の空白が含まれる場合があります(タブなど):

ls | sed -r 's|[[:blank:]]|\\\1|g' | xargs ...

これは、GNU sedなどの-r(拡張正規表現)またはbsd sedの最新バージョン(たとえば、FreeBSD 8の前に最初にオプション "-E"を綴り、両方をサポートするFreeBSDをサポートする少なくともFreeBSD 11を介した互換性のためのr&-E)。それ以外の場合は、基本的な正規表現文字クラスブラケット式を使用して、スペースとタブ文字を[]区切り文字に手動で入力できます。

[1]これはおそらく、その回答に対するコメントまたは編集としてより適切ですが、現時点ではコメントするのに十分な評判がなく、編集のみを提案できます。上記の後者の形式(grepなし)はDick.Guertinの元の回答の動作を変更するため、直接編集はおそらく適切ではありません。

5
Juan
find . -name 'Lemon*.mp3' -print0 | xargs -­0 -i mplayer '{}' 

これは、私の場合、スペースを含む異なるファイルを削除するのに役立ちました。 mplayerでも動作するはずです。必要なトリックは引用符です。 (Linuxでテスト済み Xubunt 14.04)

5
Bendel

ls | grep mp3 | sed -n "7p" | xargs -i mplayer {}

上記のコマンドでは、xargsが各ファイルに対してmplayerを新たに呼び出すことに注意してください。これはmplayerには望ましくないかもしれませんが、他のターゲットには問題ないかもしれません。

4
A-B-B

それは、(a)たとえばレモンとは対照的に、番号7にどのように結び付いているか、および(b)ファイル名のいずれかに改行が含まれているかどうか(および、改行がある場合は名前を変更するかどうか)に依存します。

それに対処する方法はたくさんありますが、そのうちのいくつかは次のとおりです。

mplayer Lemon*.mp3

find . -name 'Lemon*.mp3' -exec mplayer {} ';'

i=0
for mp3 in *.mp3
do
    i=$((i+1))
    [ $i = 7 ] && mplayer "$mp3"
done

for mp3 in *.mp3
do
    case "$mp3" in
    (Lemon*) mplayer "$mp3";;
    esac
done

i=0
find . -name *.mp3 |
while read mp3
do
    i=$((i+1))
    [ $i = 7 ] && mplayer "$mp3"
done

ファイル名に改行が含まれている場合、readループは機能しません。他の名前は、名前に改行(スペースは言うまでもなく)があっても正しく機能します。私のお金のために、改行を含むファイル名がある場合は、改行なしでファイルの名前を変更する必要があります。ファイル名を二重引用符で囲むことは、ループが正しく機能するための鍵です。

GNU findおよびGNU xargs(またはFreeBSD(* BSD?)、またはMac OS X)がある場合は、次のように-print0および-0オプションを使用することもできます。

find . -name 'Lemon*.mp3' -print0 | xargs -0 mplayer

これは、名前の内容に関係なく機能します(ファイル名に表示できない2文字のみがスラッシュとNULであり、スラッシュはファイルパスに問題を引き起こさないため、名前区切り文字としてNULを使用するとすべてがカバーされます)。ただし、最初の6つのエントリを除外する必要がある場合は、改行ではなくNULで終了する「行」を処理するプログラムが必要です。

1つ目は、手持ちの特定のケースでは断然最もシンプルです。ただし、まだリストされていない他のシナリオをカバーするために一般化されない場合があります。

2

MacOS 10.12.x(Sierra)では、ファイル名またはサブディレクトリにスペースがある場合、次を使用できます。

find . -name '*.Swift' -exec echo '"{}"' \; |xargs wc -l
2
Byron Formwalt

xargsの質問に直接答えていないことは知っていますしかし、find-execオプションに言及する価値はあります。

次のファイルシステムがあるとします。

[root@localhost bokeh]# tree --charset assci bands
bands
|-- Dream\ Theater
|-- King's\ X
|-- Megadeth
`-- Rush

0 directories, 4 files

Findコマンドを使用して、Dream TheaterとKing's Xのスペースを処理できます。したがって、grepを使用して各バンドのドラマーを検索します。

[root@localhost]# find bands/ -type f -exec grep Drums {} +
bands/Dream Theater:Drums:Mike Mangini
bands/Rush:Drums: Neil Peart
bands/King's X:Drums:Jerry Gaskill
bands/Megadeth:Drums:Dirk Verbeuren

-execオプションでは、{}はパスを含むファイル名を表します。エスケープしたり、引用符で囲んだりする必要はありません。

-execのターミネータ(+\;)の違いは、+が1つのコマンドラインにできる限り多くのファイル名をグループ化することです。一方、\;は各ファイル名に対してコマンドを実行します。

したがって、find bands/ -type f -exec grep Drums {} +は次のようになります。

grep Drums "bands/Dream Theater" "bands/Rush" "bands/King's X" "bands/Megadeth"

find bands/ -type f -exec grep Drums {} \;は次の結果になります。

grep Drums "bands/Dream Theater"
grep Drums "bands/Rush"
grep Drums "bands/King's X"
grep Drums "bands/Megadeth"

grepの場合、これにはファイル名を出力するかしないかの副作用があります。

[root@localhost bokeh]# find bands/ -type f -exec grep Drums {} \;
Drums:Mike Mangini
Drums: Neil Peart
Drums:Jerry Gaskill
Drums:Dirk Verbeuren

[root@localhost bokeh]# find bands/ -type f -exec grep Drums {} +
bands/Dream Theater:Drums:Mike Mangini
bands/Rush:Drums: Neil Peart
bands/King's X:Drums:Jerry Gaskill
bands/Megadeth:Drums:Dirk Verbeuren

もちろん、grepのオプション-hおよび-Hは、grepの呼び出し方法に関係なく、ファイル名を出力するかどうかを制御します。


xargs

xargsは、コマンドラインでのmanファイルの方法も制御できます。

xargsはデフォルトですべての引数を1行にグループ化します。 -exec \;xargs -lを使用するのと同じことを行うために。 -tオプションは、xargsにコマンドを実行する前に出力するように指示することに注意してください。

[root@localhost bokeh]# find ./bands -type f  | xargs -d '\n' -l -t grep Drums
grep Drums ./bands/Dream Theater 
Drums:Mike Mangini
grep Drums ./bands/Rush 
Drums: Neil Peart
grep Drums ./bands/King's X 
Drums:Jerry Gaskill
grep Drums ./bands/Megadeth 
Drums:Dirk Verbeuren

-lオプションは、xargsにすべてのファイル名に対してgrepを実行するよう指示することを確認してください。

デフォルトと比較(つまり、-lオプションなし):

[root@localhost bokeh]# find ./bands -type f  | xargs -d '\n'  -t grep Drums
grep Drums ./bands/Dream Theater ./bands/Rush ./bands/King's X ./bands/Megadeth 
./bands/Dream Theater:Drums:Mike Mangini
./bands/Rush:Drums: Neil Peart
./bands/King's X:Drums:Jerry Gaskill
./bands/Megadeth:Drums:Dirk Verbeuren

xargsは、コマンドラインで使用できるファイルの数をより適切に制御します。 -lオプションにコマンドごとの最大ファイル数を指定します。

[root@localhost bokeh]# find ./bands -type f  | xargs -d '\n'  -l2 -t grep Drums
grep Drums ./bands/Dream Theater ./bands/Rush 
./bands/Dream Theater:Drums:Mike Mangini
./bands/Rush:Drums: Neil Peart
grep Drums ./bands/King's X ./bands/Megadeth 
./bands/King's X:Drums:Jerry Gaskill
./bands/Megadeth:Drums:Dirk Verbeuren
[root@localhost bokeh]# 

-l2が原因で、grepが2つのファイル名で実行されたことを確認してください。

2
shrewmouse

この投稿の具体的なタイトルを考えると、ここに私の提案があります:

ls | grep ' ' | tr ' ' '<' | sed 's|<|\\ |g'

アイデアは、ブランクを '<'などの任意の固有の文字に変換し、それをバックスラッシュの後にブランクが続く '\'に変更することです。その後、次のような任意のコマンドにパイプすることができます。

ls | grep ' ' | tr ' ' '<' | sed 's|<|\\ |g' | xargs -L1 GetFileInfo

ここで重要なのは、「tr」コマンドと「sed」コマンドです。また、「?」など、「<」以外の任意の文字を使用できますまたはタブ文字も。

1
Dick.Guertin

代替ソリューションが役立つ場合があります...

Perlを使用して行の末尾にヌル文字を追加してから、xargsで-0オプションを使用することもできます。 xargs -d '\ n'(承認済みの回答)とは異なり、これはOS Xを含むすべての場所で機能します。

たとえば、スペースやその他の面白い文字が含まれている可能性のあるJPEG画像を再帰的に一覧表示(実行、移動など)するには、次のようにします。

find . | grep \.jpg | Perl -ne 'chop; print "$_\0"' | xargs -0  ls

(注:フィルター処理では、覚えやすい「| grep」構文を「find's」--name引数よりも優先します。)

0
Charlie Dalsass