私は特定のファイルにsedを適用し、変更されたファイルを一覧表示して、どのファイルが変更されたかを知ることができるスクリプトを作成しています。
これが私がsedを見つけて使用した方法です。
find . -type f -a \( -name "*.txt" -o -name "*.git"\) -a -exec sed -i -e "s/"str1"/"str2"/g" {} +
変更されたファイルのファイル名をどのように出力しますか?読みやすく整列した順序で印刷したいです。
sedのみを使用している場合は、次のことができます。
sed -i 's/$pattern/$new_pattern/w changelog.txt' $filename
if [ -s changelog.txt ]; then
# CHANGES MADE, DO SOME STUFF HERE
else
# NO CHANGES MADE, DO SOME OTHER STUFF HERE
fi
しかし、findとsedを同時に使用する場合はどうすればよいですか?マニュアルページをチェックしてたくさん試してみましたが、何も機能しませんでした。
答え1
sed -i
s
スクリプトのコマンドがsed
成功したかどうかにかかわらず、ファイルを再作成します(効果的にファイルの完全な新しいコピーを作成します)。
sed -i
ここでは、.:を含めずにGNUツールを使用したくないとしますstr1
。
find . -type f \( -name "*.txt" -o -name "*.git" \) -size +3c \
-exec grep -lZ str1 {} + |
while IFS= read -rd '' file; do
sed -i 's/str1/str2/g' "$file" &&
printf '%s\n' "$file"
done
成功した場合(新しいバージョンのファイルの作成中にエラーは発生しません)、ファイル名を含むsed
各ファイルに対して1つずつ実行し、str1
ファイル名を印刷します。sed
または、ファイルごとにgrep
1つずつ実行できますsed
。
find . -type f \( -name "*.txt" -o -name "*.git" \) \( -size +3c \
-exec grep -q str1 {} \; \
-exec sed -i 's/str1/str2/g' {} \; \
-printf '"%p" was modified\n' \
-o -printf '"%p" was not modified\n"' \)
答え2
あなたのsed
命令(正確に引用):
sed 's/str1/str2/g'
str1
これにより、すべての項目がに変わりますstr2
。含まれているファイルのリストはstr1
以下から入手できますgrep -l 'str1'
。
find . -type f \( -name '*.txt' -o -name '*.git' \) \
-exec grep -l 'str1' {} \; \
-exec sed -i 's/str1/str2/g' {} + >changelist.txt
ここではgrep -l
リダイレクトが提供されます。これは、そのパターンを含むファイルでのみ実行されるフィルタとしても機能changelist.txt
します。これにより、変更がファイルに適用されます(静かに残ります)。sed
sed
sed -i
または、find
文字列を含むファイルのパス名を印刷してみましょう。
find . -type f \( -name '*.txt' -o -name '*.git' \) \
-exec grep -q 'str1' {} \; \
-print \
-exec sed -i 's/str1/str2/g' {} + >changelist.txt
関連:
答え3
私がこの質問に答える方法は次のとおりです。 先行は達成するのが難しい楽しみのために。だからあなたがこれが好きなら、あなたは彼に投票する必要があります。これは、 find を一度だけ呼び出しながら複数のコマンドを実行し、より多くの複雑さを処理する方法を示すため、少し異なります。
回答
一致するものが見つかると、GrepはTrueと評価されます(例:$?== 0)。したがって、grep -l 'str1' filename
ファイル名にstr1がある場合も同様です。このコマンドをsedコマンドに接続すると、&&
grepが一致した場合にのみsedを実行できるようになります。
次のコマンドは、sed が変更を希望する場合にのみファイル名を出力します。
grep -l 'str1' filename && sed -i 's/str1/str2/g' filename
-execでは直接使用できないため、&&
bash呼び出しでラップします。
find ./ -type f \( -name '*.txt' -o -name '*.git' \) \
-exec bash -c "grep -l 'str1' {} && sed -i 's/str1/str2/g' {}" \; > changelist.txt
これと Kusalananda の答えの間の明らかな違いは、grep が str1 と一致しない場合、sed が実行されないことです。 Kusalanandaの答えでは、grepは各ファイルに対して実行され、sedは各ファイルに対して実行されます。ファイルの数によっては、実行時間に大きな影響を与える可能性があります。 OPの質問によると、おそらく全く大きな違いはありません。
あなたできるgrep -q
に置き換えgrep -l
、+
に置き換え、削除して、\;
彼の答えを簡素化します-print
。
find . -type f \( -name '*.txt' -o -name '*.git' \) \
-exec grep -l 'str1' {} \; \
-exec sed -i 's/str1/str2/g' {} \; >changelist.txt
これらすべては単なる叱責です。次のステップはbash -c
in find-exec
オプションを使用する理由です。誰かが役に立つと思います。
私が近づく理由
私はsedをfindと一緒に使用してログファイルの一部を印刷し、sedが何でも出力する場合にのみログファイル名を印刷したいので、ここに来ました。
次の内容を含むログがあります。
---- lots of lines before ----
Failed: 0
--------------------------------------------------
Summary
--------------------------------------------------
( Cases/Passed/Failed)
Frequency Test : ( 69/ 67/ 2)
Carrier/Data Null Test : ( 14/ 13/ 1)
Total Harmonic Distortion: ( 9/ 9/ 0)
Spur Test : ( 0/ 0/ 0)
--------------------------------------------------
failed Test
--------------------------------------------------
freq, rf2, 0.750e9, -70.0, pm 500, pm 1.0
---- lots of lines after ----
sedがテスト要約を検出した場合にのみ、テスト要約とファイル名を印刷したいと思います。
したがって、複数のファイルに対して次のような出力が必要です。
File: ./4662-0003-05132021-0953.log
Summary
--------------------------------------------------
( Cases/Passed/Failed)
Frequency Test : ( 69/ 0/ 69)
Carrier/Data Null Test : ( 14/ 0/ 14)
Total Harmonic Distortion: ( 9/ 9/ 0)
Spur Test : ( 0/ 0/ 0)
File: ./4745-0001-05132021-1017.log
Summary
--------------------------------------------------
( Cases/Passed/Failed)
Frequency Test : ( 69/ 68/ 1)
Carrier/Data Null Test : ( 14/ 14/ 0)
Total Harmonic Distortion: ( 9/ 9/ 0)
Spur Test : ( 0/ 0/ 0)
私は次のコマンドでこれを達成しました。
find ./ -type f -name '*.log' \
-exec bash -c "grep -q Summary {} && echo 'File: {}' && sed -n '/Summary/,/Spur/p' {} && echo" \;
分割しgrep -q Summary ()
、要約がログファイルに表示されない場合、次のエントリは実行されません。 sed -n '/Summary/,/Spur/p'
「Summary」と「Spur」の間のログ部分のみが印刷されます。
-exec cmd{}と-exec cmd+の違い
\;
の代わりに使う理由が気になるかもしれません+
。を使用すると、+
{}はコマンドラインに入力できるだけのファイル名に置き換えられます。これは私たちが望むものではなく、findはこの場合でもそれを許可しません。
人々から発見されました:
-exec command {} +
This variant of the -exec action runs the specified command on the selected files, but the command line is built by
appending each selected file name at the end; the total number of invocations of the command will be much less than
the number of matched files. The command line is built in much the same way that xargs builds its command lines.
Only one instance of `{}' is allowed within the command. The command is executed in the starting directory. If
find encounters an error, this can sometimes cause an immediate exit, so some pending commands may not be run at
all. This variant of -exec always returns true.
結論として
残念なことなので申し訳ありませんが、誰かに役立つことを願っています。
答え4
exec
目的のタスクを実行する小さなスクリプトを作成し、そのスクリプトをfind
。スクリプトはすでにあります。$filename
に置き換えると、$1
すでにスクリプトがあるのです。あなたのスクリプトは次の形式を取ります。
#!/bin/bash
sed -i 's/$pattern/$new_pattern/' $1
echo $1 >> changelog
このスクリプトをと呼びましょうed_notify
。これで、次のように選択したファイルで実行できます。
cat changelog >> changelog.old
rm changelog
find . -type f -a \( -name "*.txt" -o -name "*.git"\) -a -exec ed_notify {} \;