2016-03-29 8 views
2

私は、自動的に次の操作を実行できるスクリプトを作りたい:ファイルの上に複数のgrepを作成するスクリプトを作成するには?

grep 'string1' file.txt | grep 'string2' | grep 'string3' ... | grep 'stringN' 

アイデアは、スクリプトは次のように実行することができるということである。

myScript.sh file.txt string1 string2 string3 ... stringN 

、スクリプトがすべての行を返すことがありますすべての文字列を含むfile.txtです。例えば

file.txtは次のように見える場合:

hello world 
hello world run 
hello planet world 

そして、私はこのようなgrepのを行うことができます。

grep hello file.txt | grep world 

と私が手:

hello world 
hello world run 
hello planet world 

私がしたいです定義されていない数の文字列をパラメータとして、これを自動的に作成するスクリプトを作成します。

文字列の数が変わる可能性があるので、これを達成するのは難しいことがわかりました。まず、私はmyScript.shにこのようargsと呼ばれる配列を作成しようとしました:

#!/bin/bash 
args=("[email protected]") 

引数を格納する目的で。私は${args[0]}が私のfile.txtになることを知っています。残りの部分は、別のgrepsで使用する必要がある文字列ですが、問題を解決するための最善の方法であるかどうかはわかりません。私はこれをどのようにプログラムするかについての提案を感謝します。

答えて

2

sedは、1つのプロセスでこれを完全に実行することができ、これらのevalの詐欺を回避します。結果として得られるスクリプトは、実際には非常に簡単です。

#!/bin/sh 
file=$1 
shift 
printf '\\?%s?!d\n' "[email protected]" | 
sed -f - "$file" 

式ごとにsedというスクリプトを生成します。式が見つからない場合は(!)、この入力行を削除し(d)、次の入力行からやり直します。

これはsed-fへの引数として-を受け入れ、標準入力からスクリプトを読み取ることを前提としています。これは完全に移植可能ではありません。これが問題であれば、生成されたスクリプトを一時ファイルに格納する必要があります。

内部正規表現のセパレータとして?を使用しています。いずれかのパターンにリテラル?が必要な場合は、バックスラッシュでエスケープする必要があります。一般的なケースでは、検索式のいずれにもない代替セパレータを見つけるスクリプトを作成することは可能かもしれませんが、その時点で、適切なスクリプト言語(Pythonが私の好みになるでしょう)に移動します。

+0

私はあなたのコードをmyScritpt.shというファイルに貼り付けてmyScript.sh file.txt string1を実行するので、あなたのスクリプトをどのように実行するのかわかりません。string1 ... stringNですが、動作しません。 #!/ bin/bashの代わりに#!/ bin/shを書いた理由を知っているので、これはbashスクリプトで行うことができ、この単純なケースではPythonを使う必要はないと思います。 – neo33

+0

Bashは 'sh' aka Bourneシェルのスーパーセットです。このスクリプトにはBash固有の構文はありませんが、あなたが何とかもっと快適になるようにするならば、もちろん、ShebangをBashを指すように変更することは自由です。いずれにしても、 '。/ myScript.sh'を使ってスクリプトを' chmod + x'の後に実行するとよいでしょう(対話的なコマンドのための明示的な '.sh'拡張を勧めていますが、 Bourne、Bashではなく、スクリプト)。 – tripleee

+0

このスクリプトは単純ですが完全には堅牢ではありません。あなた自身の個人的な使用のために、そして/またはその欠点と限界が正しく文書化されているので、それはそれが良い方法です。パブリック配布用のツールでは、PythonやPerlに移りました。なぜなら、ユーザが提供するスクリプトの断片を使って 'sed'が完全に頑強にするのは難しいからです。 – tripleee

1

あなたが操作のパターンを生成し、変数に保存することができます:

pattern="$(printf 'grep %s file.txt' "$1"; printf ' | grep %s' "${@:2}" ; printf '\n')" 

、その後

eval "$pattern" 

例:

% cat file.txt               
foo bar 
bar spam 
egg 

% grep_gen() { pattern="$(printf 'grep %s file.txt' "$1"; printf ' | grep %s' "${@:2}" ; printf '\n')"; eval "$pattern" ;} 

% grep_gen foo bar       
foo bar 
+0

おかげで、このアプローチは非常に便利ですが、私はパターン変数内の私のファイルの名前を記述する必要があり、アイデアはbash myScript.sh file.txt string1を実行することです... stringN、私もスクリプトのパラメータとしてファイルの名前を受け取る必要があります。 – neo33

1

あなたがコマンドを作成することができますループの中でevalを使ってiを評価するt。 catを使用していますので、すべてgrepをグループ化できます。

#! /bin/bash 

file="$1" 
shift 
args=("[email protected]") 


cmd="cat '$file'" 
for a in "${args[@]}" 
do 
    cmd+=' | ' 
    cmd+="grep '$a'" 
done 

eval $cmd 
+0

これはまさに私が欲しかったことです、本当にありがとうございました。 – neo33

+0

いつものように、 'eval'はセキュリティ問題になるでしょう。たとえば、引数の1つに単一引用符が含まれている場合、これは不確実なエラーメッセージで失敗します。おそらく、より興味深い方法でそれを分解する方法があります。たとえば、起動するユーザが持つ可能性のある 'sudo'権限を利用する方法があります。 Diegoのコードに従って – tripleee

+0

私は私の最適化をしました。 – neo33

1

EVAL-無料の代替:

#!/bin/bash 

temp1="$(mktemp)" 
temp2="$(mktemp)" 

grep "$2" "$1" > temp1 
for arg in "${@:3}"; do 
    grep "$arg" temp1 > temp2 
    mv temp2 temp1 
done 

cat temp1 
rm temp1 

mktempは、一意の名前でテンポラリファイルを生成し、その名前を返します。広く利用できるはずです。

ループは、各引数に対してgrepを実行し、次のループの2番目の一時ファイルの名前を変更します。

+0

代わりに、一時的なスクリプトファイルにパイプラインを1つ生成して、より簡単で高速なスクリプトを作成することができます。 – tripleee

+0

@ tripleee私はそのようなことをすることを考えましたが、スクリプトをどのように実行しますか? –

+0

'sh/tmp/script tripleee

1

これは、ディエゴ・トレスミラノのコードと私の元の質問への答えの最適化である:

#! /bin/bash 
file=$1 
shift 
cmd="cat '$file'" 
for 'a' in "[email protected]" 
do 
    cmd+=" | grep '$a'" 
done 
eval $cmd 
+0

最適な最適化。ファイル名に空白が含まれている場合は、 '' $ file ''の前後の引用符を忘れないでください。 '' $ @ ''と'' $ a''と同じです。 –

+0

サポートありがとう、私は今理解しています。引用符の使用法は、これは非常に便利でした。 – neo33

関連する問題