2017-06-30 9 views
0

を変更してしまったファイルや行を報告して操作を検索と置換 - 各行にバッシュ - 私は以下のように入力ファイル「test.txtという」を持っている

hostname=abc.com hostname=xyz.com 
db-host=abc.com db-host=xyz.com 

を、スペース前の値が古い値であります"test"という名前のフォルダ内のスペースの後に新しい値で置き換える必要があります。私は以下のシェルスクリプトを使ってこれを行うことができます。

"sed"は、100sのファイルでその文字列をオンザフライで置き換えます。

私はファイルの絶対パスのように変更されたファイルのレポートを得ることができるトリックまたは代替方法はありますか?&変更された正確な行はありますか?

PS - 私はsedまたはストリームエディタがこの機能をサポートしていないことを理解しています。私はバージョニングを使用したくないのですが、この作業のために過度な作業になるでしょう。 man sedから

答えて

2

は、置換値の広い範囲を扱う時に、それは少しより堅牢にするために、スクリプトの簡単な書き換えで始まるのをみましょう、だけでなく、より速く:

#!/bin/bash 

# escape regexp and replacement strings for sed 
escapeRegex() { sed 's/[^^]/[&]/g; s/\^/\\^/g' <<<"$1"; } 
escapeSubst() { sed 's/[&/\]/\\&/g' <<<"$1"; } 

while read -r old new; do 
    find test -type f -exec sed "/$(escapeRegex "$old")/$(escapeSubst "$new")/g" -i '{}' \; 
done <test.txt 

そこで、我々test.txtからのラインで空白で区切られたフィールド(oldnew)のペアをループとは、インプレース標準sedfindで見つかったすべてのファイルに置き換えて実行します。あなたのスクリプトにかなり似て

が、test.txtから我々properly read lines(なし単語の分割、パス名/変数の展開、など)、我々は可能な限り(catなどの外部ツールを呼び出す必要はありません、cutxargs)バッシュの組み込みコマンドを使用します。 escape sed metacharactersold/newsedのregexpと置き換え式として正しく使用するための値です。

今度はlogging from sedを追加してみましょう。

#!/bin/bash 

# escape regexp and replacement strings for sed 
escapeRegex() { sed 's/[^^]/[&]/g; s/\^/\\^/g' <<<"$1"; } 
escapeSubst() { sed 's/[&/\]/\\&/g' <<<"$1"; } 

while read -r old new; do 
    find test -type f -printf '\n[%p]\n' -exec sed "/$(escapeRegex "$old")/{ 
     h 
     s//$(escapeSubst "$new")/g 
     H 
     x 
     s/\n/ -->/
     w /dev/stdout 
     x 
    }" -i '{}' > >(tee -a change.log) \; 
done <test.txt 

上記sedスクリプトがnewに各oldを変更しますが、それはまた、我々は順番にchange.logファイルに追加/dev/stdout(バッシュ固有)、へold --> new行を書き込みます。 find-printfアクションは、処理されるファイルごとにファイル名の「ヘッダー」行を出力します。ただ、完全性について

[file1] 
hostname=abc.com --> hostname=xyz.com 

[file2] 

[file1] 
db-host=abc.com --> db-host=xyz.com 

[file2] 
db-host=abc.com --> db-host=xyz.com 

、迅速なウォークスルーsedスクリプト:これにより

、あなたの「変更履歴」のようになります。我々は、old値を含む行にのみ作用する。そのような行ごとに、スペース(h)を格納するために格納し、newに変更し、その新しい値を今度はold\nnewを保持する保持スペース(改行で結合された、H)に追加します。パターンスペース(x)を保持するので、old --> newに変換するsコマンドを実行することができます。それをに書き込んだ後、newをホールドからパターンスペースに戻して、処理されたファイルに書き込まれます(インプレース)。

+0

素晴らしいですが、うまくいきます。ありがとう! – vivekyad4v

2

-i[SUFFIX], --in-place[=SUFFIX] 
      edit files in place (makes backup if SUFFIX supplied) 

これは交換時にバックアップファイルを作成するために使用することができます。次に、どのファイルが変更されたかを示すバックアップファイルと、元のファイルを持つdiffを検索できます。 diffを調べたら、バックアップファイルを削除するだけです。

あなたはsed文ではなく、あなたはさらに、1つを移動し、sedシェバング行のいずれかを使用するか、一回の操作ですべての置換を行うために-f/--fileにファイルを渡すことができ、カスタムフォーマットとして、あなたの代替品を策定した場合。

+0

いい考えですが、私は考えていましたが、1つのファイルに複数の変更がある場合は管理が複雑になります。 "sed"は1.bak.bak.bakのような複数の ".bak"エクステンションを持つ多数のファイルを作成します。 – vivekyad4v

0

あり、スクリプトにはいくつかの問題だ、ちょうど(インプレース編集のためのsedの代わりにGNUのGNUのawkを使用して)とそれをすべて置き換える:

mapfile -t files < <(find test -type f) 
awk -i inplace ' 
NR==FNR { map[$1] = $2; next } 
{ for (old in map) gsub(old,map[old]) } 
' test.txt "${files[@]}" 

あなたはそれがより速くあなたが何であったかよりも桁違いに見つけることができますやって

"test.txt"文字列にregexpまたはbackreferenceメタ文字が含まれていて、以前に変更された文字列を修正して部分一致を処理すると、既存のスクリプトが失敗するという問題があります。 awkで回避してください(そしてsedで非常に難しい!)。

レポートを取得するには、{ for ... }行を調整するだけです。標準エラー出力への変更の記録印刷する:

mapfile -t files < <(find test -type f) 
awk -i inplace ' 
NR==FNR { map[$1] = $2; next } 
{ 
    orig = $0 
    for (old in map) { 
     gsub(old,map[old]) 
    } 
    if ($0 != orig) { 
     printf "File %s, line %d: \"%s\" became \"%s\"\n", FILENAME, FNR, orig, $0 | "cat>&2" 
    } 
} 
' test.txt "${files[@]}" 
関連する問題