web-dev-qa-db-ja.com

xargsにバイナリではなくエイリアスを使用させる

CentOS6.5のBash4.2:

私の~/.bash_profileには、次のようなエイリアスがたくさんあります。

alias grep='grep -n --color=always'

grepを実行すると、色の強調表示を取得し、行番号を自動的に印刷できます。以下を実行すると、強調表示は期待どおりに機能します。

$ grep -Re 'regex_here' *.py

ただし、最近これを実行したとき:

$ find . -name '*.py' | xargs grep -E 'regex_here'

結果は強調表示されず、行番号も出力されなかったため、戻って-n --color=alwaysgrepコマンドに明示的に追加する必要がありました。

  • xargsは環境内のエイリアスを読み取りませんか?
  • そうでない場合、それを行う方法はありますか?
14
MattDMo

エイリアスは、それが定義されているシェルの内部にあります。他のプロセスには表示されません。シェル関数についても同様です。 xargsは独立したアプリケーションであり、シェルではないため、エイリアスや関数の概念はありません。

grepを直接呼び出す代わりに、xargsにシェルを呼び出させることができます。ただし、シェルを呼び出すだけでは不十分であり、そのシェルでもエイリアスを定義する必要があります。エイリアスが.bashrcで定義されている場合は、そのファイルを入手できます。ただし、これは機能しない可能性があります。.bashrcは、非対話型シェルでは意味をなさない他のタスクを実行します。

find . -name '*.py' | xargs bash -c '. ~/.bashrc; grep -E regex_here "$@"' _

正規表現を入力するときは、ネストされた引用の複雑さに注意してください。正規表現をパラメーターとしてシェルに渡すことで、作業を簡略化できます。

find . -name '*.py' | xargs bash -c '. ~/.bashrc; grep -E "$0" "$@"' regex_here

エイリアス検索は明示的に実行できます。次に、xargsにはgrep -n --color=alwaysが表示されます。

find . -name '*.py' | xargs "${BASH_ALIASES[grep]}" regex_here

Zshの場合:

find . -name '*.py' | xargs $aliases[grep] regex_here

ちなみに、find … | xargs …はスペースを含むファイル名で壊れることに注意してください(とりわけ) 。これは、nullで区切られたレコードに変更することで修正できます。

find . -name '*.py' -print0 | xargs -0 "${BASH_ALIASES[grep]}" regex_here

または-execを使用して:

find . -name '*.py' -exec "${BASH_ALIASES[grep]}" regex_here {} +

findを呼び出す代わりに、シェル内ですべてを行うことができます。グロブパターン**/は、ディレクトリを再帰的にトラバースします。 bashでは、最初にshopt -s globstarを実行してこのグロブパターンを有効にする必要があります。

grep regex_here **/*.py

これにはいくつかの制限があります。

  • 多くのファイルが一致する場合(またはパスが長い場合)、コマンドラインの最大長を超えているためにコマンドが失敗する可能性があります。
  • Bash≤4.2では(ただし、最近のバージョンでも、kshまたはzshでも)、**/はディレクトリへのシンボリックリンクに再帰します。

別のアプローチは、 MariusMatutiae によって提案されているように、プロセス置換を使用することです。

grep regex_here <(find . -name '*.py')

これは、**/が適用されない場合に役立ちます。複雑なfind式の場合、またはbash≤4.2では、シンボリックリンクで再帰しない場合に便利です。これは、スペースを含むファイル名で壊れることに注意してください。回避策は、 IFSを設定し、globbing を無効にすることですが、少し複雑になり始めています。

(IFS=$'\n'; set -f; grep regex_here <(find . -name '*.py') )

使用する alias xargs='xargs '

alias: alias [-p] [name[=value] ... ]
(snip)
A trailing space in VALUE causes the next Word to be checked for
alias substitution when the alias is expanded.
11
1.61803

これを、関連する SO質問 で見つけることができない別のアプローチのデモとして見てください。

xargsのラッパー関数を記述して、最初の引数がエイリアスであるかどうかを確認し、エイリアスである場合はそれに応じて展開します。

これはまさにそれを行うコードですが、残念ながらZ Shellが必要であるため、bashと1:1で実行されません(率直に言って、私はbashを使用するのに十分に使用されていない):

xargs () {
        local expandalias
        if [[ $(which $1) =~ "alias" ]]; then
                expandalias=$(builtin alias $1) 
                expandalias="${${(s.'.)expandalias}[2]}"
        else
                expandalias=$1
        fi
        command xargs ${(z)expandalias} "${(z)@[2,-1]}"
}

それが機能することの証明:

zsh% エイリアスgrep = "grep -n" ´                          #一致する行番号を含める
 zsh% foo -name "* .p *"を検索| xargs grep-Eテスト
 foo/bar.p0:151:#data = test 
 foo/bar.p1:122:#data = test#行番号が含まれています
 zsh% unalias grep
 zsh% find foo -name "* .p *" | xargs grep-Eテスト
 foo/bar.p0:#data = test 
 foo/bar.p1:#data = test#行番号は含まれていません
 zsh%
2
mpy

より単純でより洗練された解決策は、 プロセス置換 を使用することです。

grep -E 'regex_here' <( find . -name '*.py')

パイプのように新しいシェルは作成されません。つまり、エイリアスが定義されている元のシェルにとどまり、出力は希望どおりになります。

リダイレクトと括弧の間にスペースを入れないように注意してください。そうしないと、bashがエラーをスローします。私の知る限り、プロセス置換はBash、Zsh、Ksh {88,93}でサポートされていますが、pdkshではサポートされていません(まだ)。

1
MariusMatutiae

grepは、環境変数GREP_OPTIONSからデフォルトオプションのセットを読み取ります。あなたが置く場合

 export GREP_OPTIONS='--line-number --color=always'

.bashrcで、変数がサブシェルに渡され、期待どおりの結果が得られます。

0
doneal24