web-dev-qa-db-ja.com

xargsは引数ごとにサブシェルコマンドを実行できますか?

ファイルのUUIDを生成しようとしているコマンドがあります。

_find -printf "%P\n"|sort|xargs -L 1 echo $(uuid)
_

しかし、結果として、xargs$(uuid)サブシェルを1回だけ実行します。

_8aa9e7cc-d3b2-11e4-83a6-1ff1acc22a7e file1
8aa9e7cc-d3b2-11e4-83a6-1ff1acc22a7e file2
8aa9e7cc-d3b2-11e4-83a6-1ff1acc22a7e file3
_

xargsを取得して各入力でサブシェルコマンドを実行するためのワンライナー(つまり関数ではない)はありますか?

14
adelphus

これは、$(uuid)が現在のシェルで展開されるためです。シェルを明示的に呼び出すことができます。

find -printf "%P\n"| sort | xargs -I '{}' bash -c 'echo $(uuid) {}'

ところで、私は次のコマンドを使用します:

find -exec bash -c 'echo "$(uuid) ${1#./}"' -- '{}' \;

xargsなし。

18
hek2mgl

Forループの場合:

for i in $(find -printf "%P\n" | sort) ; do echo "$(uuid) $i";  done

編集:これを行う別の方法:

find -printf "%P\0" -exec uuid -v 4 \; | sort | awk -F'\0' '{ print $2 " " $1}'

これにより、ファイル名の後に、ソートを実行させるためのuuid(サブシェルは不要)が出力され、nullで区切られた2つの列が交換されます。

3
guido

hek2mglの答え 問題をうまく説明し、彼の解決策はうまく機能します。この答えはパフォーマンスを見ています。

受け入れられた答えは、すべての入力行に対してbashプロセスを作成するため、少し遅いです。

xargsは一般にシェルコードループよりも速く、より望ましいですが、この特定のケースでは、各反復でシェル機能が必要になるため、役割が逆になります

次の代替ソリューションは、whileループを使用して入力行を処理します。私のマシンでは、xargsの約2倍の速度です。解決。

find . -printf "%P\n" | sort | while IFS= read -r f; do echo "$(uuid) $f"; done
  • whileではなくforを使用していることに注意してください。これは、forがコマンド出力を確実に解析できないためです(つまり、空白が埋め込まれたファイル名はコマンドを壊してしまいます。 httpを参照) ://mywiki.wooledge.org/DontReadLinesWithFor )。

改行が埋め込まれたファイル名(非常にまれ)が心配で、[〜#〜] gnu [〜#〜]ユーティリティを使用する場合は、NULバイトを区切り文字として使用できます。

find . -printf "%P\0" | sort -z | while IFS= read -d '' -r f; do echo "$(uuid) $f"; done

更新最速アプローチは、シェルループをまったく使用しないことです。 ᴳᵁᴵᴰᴼの巧妙な答えによって証明されます。彼の答えのポータブルバージョンについては、以下を参照してください。


互換性に関する注意:

OPのfindコマンドは、[〜#〜] gnu [〜#〜]find(Linux)の使用を意味し、機能( -printf)他のプラットフォームでは機能しない可能性があります。

これがポータブルバージョンの ᴳᵁᴵᴰᴼの答えで、find(およびawk)のPOSIX準拠の機能のみを使用しています。
ただし、uuidはPOSIXユーティリティではないことに注意してください。 LinuxおよびBSDのようなシステム(OSXを含む)にはuuidgenユーティリティがあるため、コマンドは代わりにそれを使用します。

 find . -exec printf '%s\t' {} \; -exec uuidgen \; | 
   awk -F '\t' '{ sub(/.+\//,"", $1); print $2, $1 }' | sort -k2
3
mklement0