find -exec呼び出しでのユーザー定義関数の実行


25

私はSolaris 10を使用していますが、ksh(88)、bash(3.00)、およびzsh(4.2.1)で以下をテストしました。

次のコードは結果を生成しません。

function foo {
    echo "Hello World"
}

find somedir -exec foo \;

(交換することによって示されるように検索には、いくつかのファイルが一致しない-exec ...-print)、およびから外部に呼び出されたときに機能が完璧に動作findコール。

man findページの内容は-exec次のとおりです。

 -exec command実行されたコマンドが
                     終了ステータスとしてゼロ値。の終わり
                     コマンドはエスケープされた
                     セミコロン(;)。コマンド引数{}は
                     現在のパス名に置き換えられます。もし
                     -execの最後の引数は{}であり、あなたは
                     セミコロン(;)ではなく+を指定します。
                     コマンドの呼び出し回数が少ない
                     {}はパス名のグループに置き換えられました。もし
                     コマンドを呼び出すと、
                     終了ステータスとしてゼロ以外の値、検索
                     ゼロ以外の終了ステータスを返します。

私はおそらくこのようなことをして逃げることができます:

for f in $(find somedir); do
    foo
done

しかし、私はフィールドセパレーターの問題に対処することを恐れています。

呼び出しからシェル関数(同じスクリプトで定義され、スコープの問題に煩わされないようにする)をfind ... -exec ...呼び出すことは可能ですか?

私は両方でそれを試してみました/usr/bin/findし、/bin/findそして同じ結果を得ました。


関数を宣言した後に関数をエクスポートしようとしましたか?export -f foo
h3rrmiller

2
関数を外部スクリプトにして、に配置する必要がありPATHます。または、使用してsh -c '...'、両方を定義し、...ビットで関数を実行します。関数とスクリプトの違いを理解するのに役立つかもしれません。
jw013

回答:


27

A functionはシェルに対してローカルであるため、使用するにはfind -exec、シェルを生成し、そのシェルでその関数を定義する必要があります。何かのようなもの:

find ... -exec ksh -c '
  function foo {
    echo blan: "$@"
  }
  foo "$@"' ksh {} +

bashを使用して環境を介して関数をエクスポートexport -fできるため、次のことができます(bashで):

foo() { ...; }
export -f foo
find ... -exec bash -c 'foo "$@"' bash {} +

ksh88持っているtypeset -fxエクスポート機能(ではない環境を介して)、それは彼女だけ-強打レスで実行されるスクリプトで使用することができますkshとそうではありません、ksh -c

別のオプションは次のとおりです。

find ... -exec ksh -c "
  $(typeset -f foo)"'
  foo "$@"' ksh {} +

つまり、インラインスクリプト内の関数typeset -fの定義をダンプするために使用しfooます。foo他の関数を使用する場合は、それらもダンプする必要があることに注意してください。


2
あなたは2回出現がある理由を説明できますkshbash-execコマンドが?私は最初の出来事を理解していますが、2番目の出来事は理解していません。
ダニエルクルマン

6
@danielkullmann In bash -c 'some-code' a b c$0is a$1is b...ですので、なりたい場合は、前に何かを挿入する必要$@がありa, b, cます。$0エラーメッセージを表示するときにも使用されるため、シェルの名前、またはそのコンテキストで意味のある名前を使用することをお勧めします。
ステファンシャゼル

@StéphaneChazelas、そんな素敵な答えをありがとう。
User9102d82

5

これは常に適用できるわけではありませんが、適用できる場合は簡単な解決策です。globstarオプションを設定します(set -o globstarksh93ではshopt -s globstarbash≥4、zshではデフォルトでオンになっています)。次に**/、現在のディレクトリとそのサブディレクトリを再帰的に一致させるために使用します。

たとえば、の代わりにfind . -name '*.txt' -exec somecommand {} \;、次を実行できます

for x in **/*.txt; do somecommand "$x"; done

の代わりにfind . -type d -exec somecommand {} \;、実行することができます

for d in **/*/; do somecommand "$d"; done

の代わりにfind . -newer somefile -exec somecommand {} \;、実行することができます

for x in **/*; do
  [[ $x -nt somefile ]] || continue
  somecommand "$x"
done

使用**/できない場合(シェルにない場合、またはfindシェルに類似していないオプションが必要なため)、引数で関数を定義しfind -execます


使用しているglobstarksh(ksh88)のバージョンにオプションが見つからないようです。
ラーム

@rahmu確かに、ksh93の新機能です。Solaris 10のどこかにksh93がありませんか?
ジル「SO-悪であるのをやめる」

このブログ投稿によると、Solaris 11では、Bourne Shellとksh88の両方を置き換えるためにksh93が導入されています...
rahmu

@rahmu。Solarisにはしばらくの間dtksh(一部のX11拡張を備えたksh93)ksh93がありましたが、古いバージョンであり、CDEがオプションのオプションパッケージの一部である可能性があります。
ステファンシャゼル

再帰的findグロビングは、ドットファイルを除外してdotdirに下がらず、ファイルリストを並べ替えるという点で異なります(どちらもグロブ修飾子を介してzshのアドレスになります)。また、と**/*は対照的に./**/*、ファイル名はで始まります-
ステファンシャゼル

4

\ 0を区切り文字として使用し、次のように、生成されたコマンドからファイル名を現在のプロセスに読み込みます。

foo() {
  printf "Hello {%s}\n" "$1"
}

while read -d '' filename; do
  foo "${filename}" </dev/null
done < <(find . -maxdepth 2 -type f -print0)

何が起きてる:

  • read -d '' 次の\ 0バイトまで読み取るため、ファイル名の奇妙な文字を心配する必要はありません。
  • 同様に、-print0\ nの代わりに\ 0を使用して、生成された各ファイル名を終了します。
  • cmd2 < <(cmd1)は、cmd1 | cmd2cmd2がサブシェルではなくメインシェルで実行されることを除いて同じです。
  • fooの呼び出しは/dev/null、パイプから誤って読み取られないようにリダイレクトされます。
  • $filename 引用符で囲まれているため、シェルは空白を含むファイル名を分割しようとしません。

今、read -dそして<(...)zshのは、bashやkshの93Uであるが、私は確か程度以前のバージョンのkshありませんよ。


4

スクリプトから生成された子プロセスで、事前定義されたシェル関数を使用する場合、エクスポートする必要があります export -f <function>

注:export -fbash固有です

シェルのみがシェル関数を実行できるため:

find / -exec /bin/bash -c 'function "$1"' bash {} \;

編集:基本的に、スクリプトは次のようになります。

#!/bin/bash
function foo() { do something; }
export -f foo
find somedir -exec /bin/bash -c 'foo "$0"' {} \;

1
export -fはの構文エラーでksh、関数定義をの画面に出力しzshます。kshzshおよびのすべてで、bash問題は解決しません。
ラーム

1
find / -exec /bin/bash -c 'function' \;
h3rrmiller

現在は動作しbashますが、のみで動作します。ありがとうございました!
rahmu

4
{}シェルコードに埋め込まないでください!ファイル名がシェルコードとして解釈されていることを意味はとても危険です
ステファンChazelas
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.