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


252
$ 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を機能させることはできますか?


代わりにls |grep mp3 |sed -n "7p"を使用できますecho "Lemon Tree.mp3"
Micha Wiedenmann 2013年


回答:


256

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

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

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

ls *.mp3 | xargs -0 mplayer

この方法はより単純で、GNU xargsでも機能します。


6
一般的な使用のためのベストアンサー!これは、前のコマンドが「検索」でなくても機能します
nexayq

28
残念ながら、このオプションは、OS X上では使用できません
トーマスTempelmann

25
@Thomas OS Xの場合、フラグはです-E。例:xargs -E '\n'

30
OS Xでは、-E '\ n'は効果がありませんでした。また、レコードセパレーターではなくeofstrを変更したため、予期していませんでした。ただし、前のコマンドが「find」でなくても、入力でfindの-print0フラグの効果をシミュレートすることにより、解決策として-0フラグを利用できました。例:ls * mp3 | tr '\ n' '\ 0' | xargs -0 mplayer
biomiker

10
OS Xの場合は、「brew install findutils」を実行できます。これにより、-dスイッチを備えた「gxargs」コマンドが表示さます。
トム・デ・レイ

213

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)"

10
これはGNU findとGNU を使用していますxargs。それらのプログラムのすべてのバージョンがこれらのオプションをサポートしているわけではありません(ただし、そうする必要がある場合もあります)。
ジョナサンレフラー2013年

1
@JonathanLeffler s / GNU / FreeBSD / g; 悲しいことにPOSIXはテキストファイルのNUL文字を恐れており、まだ十分な治療法がありません:-)私のアドバイスは、実際には移植できないオプションに頼っています。
イェンス、2013年

6
また、Mac OS X(BSD派生)にはfindwith -print0およびxargswithがあり-0ます。AFAIK、HP-UX、AIX、およびSolarisはサポートしていません(ただし、HP-UX 11iはサポートしていませんが、Solaris 10はサポートしていませんが、AIX 5.xはサポートしていませんが、現在のバージョンではありません。 )。それは変更に難しいことではないsedで終わる「行」を使用するために、例えば、'\0'代わりのを'\n'、およびPOSIX 2008はgetdelim()、それが簡単に管理するためになるだろう。
ジョナサンレフラー2013年

2
リストファイルを含むファイルパスで使用する+1 + 1トリック:cat $ file_paths_list_file | cat perl -ne 's | \ n | \ 000 | g; print' | xargs -0 zip $ zip_package
Yordan Georgiev

2
改行をNULで置き換えるのは良い考えです。GNUfindもGNU xargsもperlもない組み込みシステムでこれを行わなければなりませんでしたが、trコマンドを利用して同じことを行うことができます。cat $ file_paths_list_file | tr '\ n' '\ 0' | xargs -0 du -hms
joensson


16

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

lsを取得して1行に1つのファイルを出力し、改行をnullに変換し、xargsにnullを区切り文字として使用するように指示します。

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


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

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


7

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

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

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

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

もちろん、ファイル名には空白以外の空白(タブなど)を含めることができます。

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

これは、GNU sedなどの-r(拡張正規表現)をサポートするsedまたはbsd sedの最近のバージョン(たとえば、FreeBSD 8より前に「-E」オプションを最初につづったFreeBSDで、互換性のために-rと-Eの両方をサポートするsed)があることを前提としています。 FreeBSD 11を介して)。それ以外の場合は、基本的な正規表現文字クラスの括弧式を使用して、スペースとタブ文字を[]区切り文字に手動で入力できます。

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


1
その出力を考慮せずにファイルに名前を付けるスクリプトを実行するクレイジーなUNIXの人たち
アンドリューlorien

4

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

上記のコマンドでは、ファイルごとに新しくxargs呼び出されることに注意してくださいmplayer。これはには望ましくない場合mplayerがありますが、他のターゲットには問題ない場合があります。


1
既存の回答への便利な追加ですが、これによりmplayer、ファイルごとに新たに呼び出されることに注意してください。たとえば、次のことを試してみてください... | xargs -I{} mplayer -shuffle {}。これは、にもかかわらず、完全に確定的な順序で再生され-shuffleます。

1
通常、それは意図ではありません。xargs主にファイル名のリストを受け入れるコマンドで使用され(簡単な例rm:)、各呼び出しに収まるだけのファイル名を渡そうとし、必要な場合にのみ複数の呼び出しに分割します。echo(デフォルト)などの各呼び出しが表示されるコマンドを使用すると、違いを確認できますseq 0 100000 | xargs。0から23695(プラットフォーム固有ですが、私のシステムではこれが行われます)のすべての数値を最初の行から45539に出力します。 2行目などです。そうです、ほとんどのコマンドにとって、それは重要ではありません。

4

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

find . -name '*.swift' -exec echo '"{}"' \; |xargs wc -l

2

それは、(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場合は、次のようにand オプションを使用することもできます。

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

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

最初の方法は、手元にある特定のケースで最も単純です。ただし、まだリストされていない他のシナリオをカバーするように一般化されていない場合があります。


2

私は、私は答えていないよということを知って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]# 

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


1

この投稿の具体的なタイトルを考えると、これが私の提案です。

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

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

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

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


迂回の目的は何trですか?なぜls *.mp3 | sed -n '7!b;s/\([[:space:]]\)/\\\1/g;p'ですか?
tripleee 2016

1
「tr '' '?'」を使用すると、「sed」を使用する必要がなくなることがわかりました。シングル「?」文字は空白ではありませんが、この場合は空白の任意の1文字と一致します。.mp3で終わるすべてのファイルを処理しようとしているので、それが他の何かである確率はかなり小さく、許容できます。「ls | grep '' | tr '' '?」| xargs -L1 GetFileInfo "
Dick Guertin

「タブ」を同時に処理することもできます:tr '\ t' '??' 両方を処理します。
Dick Guertin、2016

1

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

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

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

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

(注:フィルタリングでは、「検索」する--name引数よりも覚えやすい「| grep」構文を使用します。)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.