複数のファイルを変更する


194

次のコマンドは、2つのファイルの内容を正しく変更しています。

sed -i 's/abc/xyz/g' xaa1 xab1 

しかし、私がする必要があるのは、そのようなファイルを動的に変更することであり、ファイル名がわかりません。私は始まる、現在のディレクトリからすべてのファイルを読み込みますコマンド書きたいxa*sed、ファイルの内容を変更する必要があります。


61
つまりsed -i 's/abc/xyz/g' xa*
ポールR

3
ここでの答えは十分ではありません。unix.stackexchange.com/questions/112023/…を
Isaac

回答:


135

さらに良い:

for i in xa*; do
    sed -i 's/asd/dfg/g' $i
done

ファイルがいくつあるか誰も知らないため、コマンドラインの制限を破るのは簡単です。

ファイルが多すぎると、次のようになります。

# grep -c aaa *
-bash: /bin/grep: Argument list too long
# for i in *; do grep -c aaa $i; done
0
... (output skipped)
#

18
その数のファイルがある場合、コマンドのコマンドライン制限を破りますforfind ... | xargs ...
それから

1
実装はわかりませんが、「xa *」パターンはある時点で拡張する必要があります。シェルはごとに異なる展開をするのかfor、それがためよりもechogrep
グレン・ジャックマン

4
更新された回答をご覧ください。さらに情報が必要な場合は、公式の質問をしてください。そうすれば、人々が助けてくれるでしょう。
lenik

5
sedコマンドでは、"$i"代わりに$iを使用して、スペースを含むファイル名の単語分割を回避する必要があります。そうでなければ、これはとてもいいです。
ワイルドカード2015年

4
リストに関しては、違いはfor言語構文の一部であり、単なる組み込みではないということです。の場合sed -i 's/old/new' *、の展開は*sedの引数リストとして渡される必要があり、sedプロセスを開始する前にこれを実行する必要があると確信しています。forループを使用すると、完全なarglist(の展開*)がコマンドとして渡されることはなく、シェルメモリに格納されて繰り返されるだけです。私はこれについてはまったく言及していませんが、それが違いである可能性が高いようです。(もっと知識のある人から連絡をもらいたい...)
ワイルドカード

165

一致するファイル名ごとにプロセスを開始しますが、このタイプのユースケースを対象とする-exec引数について誰も言及していないことに驚いています。

find . -type f -name 'xa*' -exec sed -i 's/asd/dsg/g' {} \;

あるいは、より少ないプロセスを呼び出すxargsを使用することもできます。

find . -type f -name 'xa*' | xargs sed -i 's/asd/dsg/g'

または、findの代わりに+ execバリアントを使用して;、findがサブプロセス呼び出しごとに複数のファイルを提供できるようにします。

find . -type f -name 'xa*' -exec sed -i 's/asd/dsg/g' {} +

7
この回答のコマンドを次のように変更する必要がありました。これfind ./ -type f -name 'xa*' -exec sed -i '' 's/asd/dsg/g' {} \;が、findコマンドの場所であり、OSXの./-iには単一引用符のペアです。
shelbydz 2017年

findコマンドは、ealfonsoから提供されたもの./と同じように機能します。これは、backupsuffixパラメーター.と同じであり、その後-iにパラメーターのみが含まれます。
uhausbrand 2018年

-exec一緒に検索のオプションが{} +述べたように問題を解決するのに十分であり、最も要件の罰金にする必要があります。ただしxargs-pオプションで並列処理もできるため、一般的にはより良い選択です。コマンドラインの長さをオーバーフローするのに十分な大きさのグロブ拡張がある場合、シーケンシャル実行よりもスピードアップの恩恵を受ける可能性があります。
アミットナイドゥ

78

grepとsedを一緒に使用できます。これにより、サブディレクトリを再帰的に検索できます。

Linux: grep -r -l <old> * | xargs sed -i 's/<old>/<new>/g'
OS X: grep -r -l <old> * | xargs sed -i '' 's/<old>/<new>/g'

For grep:
    -r recursively searches subdirectories 
    -l prints file names that contain matches
For sed:
    -i extension (Note: An argument needs to be provided on OS X)

3
私にとってのこの方法のボーナスは、grep -vgitフォルダーを回避するために私が陥ることができたということでしたgrep -rl <old> . | grep -v \.git | xargs sed -i 's/<old>/<new>/g'
Martin Lyne

Macに最適なソリューション!
マルキス

30

これらのコマンドsedは、Mac OS Xに付属するデフォルトでは機能しません。

からman 1 sed

-i extension
             Edit files in-place, saving backups with the specified
             extension.  If a zero-length extension is given, no backup 
             will be saved.  It is not recommended to give a zero-length
             extension when in-place editing files, as you risk corruption
             or partial content in situations where disk space is exhausted, etc.

試しました

sed -i '.bak' 's/old/new/g' logfile*

そして

for i in logfile*; do sed -i '.bak' 's/old/new/g' $i; done

どちらも正常に動作します。


2
@sumekすべてのオカレンスを置き換えるsedを示すOS Xのターミナルセッションの例を次に示します
。GitHub

これを使用して、すべてのWebサイト構成ファイルの2つの異なる行を以下の1行に置き換えました。sed -i.bak "s / supercache_proxy_config / proxy_includes \ / supercache_config / g; s / basic_proxy_config / proxy_include \ / basic_proxy_config / g" sites-available / *ファイルの作業が終わったら、*。bakファイルを必ず削除してくださいシステム衛生酒。
ジョサイア2015年

19

@PaulRはこれをコメントとして投稿しましたが、人々はそれを回答として見る必要があります(そしてこの回答は私のニーズに最適です):

sed -i 's/abc/xyz/g' xa*

これは、おそらく数十程度の適度な量のファイルで機能しますが、数百万程度ではありません


置換にスラッシュがあると仮定します。filepathsの別の例sed -i 's|auth-user-pass nordvpn.txt|auth-user-pass /etc/openvpn/nordvpn.txt|g' *.ovpn
レオ・レオポルド・ヘルツ준 영

10

別のより用途の広い方法は使用することfindです:

sed -i 's/asd/dsg/g' $(find . -type f -name 'xa*')

1
そのfindコマンドの出力が展開されるため、これは問題を解決しません。代わりに、-exec
ealfonso

@erjoalgo sedコマンドは複数の入力ファイルを処理できるため、これは機能します。findコマンドの拡張は、それを機能させるためにまさに必要でした。
dkinzer、2015年

ファイルの数がコマンドラインの制限を超えない限り機能します。
ealfonso

この制限は、マシンで使用可能なメモリリソースにのみ依存し、execの制限とまったく同じです。
dkinzer、2015年

4
それは単に真実ではありません。上記のコマンドでは、$(find。...)は単一のコマンドに展開されます。一致するファイルが多数ある場合は、非常に長くなる可能性があります。それが長すぎる場合(例えば、私のシステムでは制限が約2097152文字です)、「引数リストが長すぎます」というエラーが表示され、コマンドが失敗します。このエラーをグーグルして、これに関する背景情報を取得してください。
ealfonso

2

find同様のタスクに使用しています。これは非常に簡単です。次のsedように引数として渡す必要があります。

sed -i 's/EXPRESSION/REPLACEMENT/g' `find -name "FILE.REGEX"`

この方法では、複雑なループを記述する必要がなく、変更するファイルを確認するのは簡単findですsed。実行する前に実行するだけです。


1
これは@dkinzerの回答とまったく同じです。
Tao氏、

0

あなたは作ることができます

' xxxx ' text u search and replace it it with ' yyyy '

grep -Rn '**xxxx**' /path | awk -F: '{print $1}' | xargs sed -i 's/**xxxx**/**yyyy**/'

0

スクリプトを実行できる場合は、同様の状況で私が行ったことを次に示します。

sedコマンドに辞書/ハッシュマップ(連想配列)と変数を使用して、配列をループし、いくつかの文字列を置き換えることができます。ワイルドカードをに含めると、name_patternファイルのインプレースをname_pattern='File*.txt'特定のディレクトリ(source_dir)内のパターン(のようなもの)に置き換えることができます。すべての変更はlogfiledestin_dir

#!/bin/bash
source_dir=source_path
destin_dir=destin_path
logfile='sedOutput.txt'
name_pattern='File.txt'

echo "--Begin $(date)--" | tee -a $destin_dir/$logfile
echo "Source_DIR=$source_dir destin_DIR=$destin_dir "

declare -A pairs=( 
    ['WHAT1']='FOR1'
    ['OTHER_string_to replace']='string replaced'
)

for i in "${!pairs[@]}"; do
    j=${pairs[$i]}
    echo "[$i]=$j"
    replace_what=$i
    replace_for=$j
    echo " "
    echo "Replace: $replace_what for: $replace_for"
    find $source_dir -name $name_pattern | xargs sed -i "s/$replace_what/$replace_for/g" 
    find $source_dir -name $name_pattern | xargs -I{} grep -n "$replace_for" {} /dev/null | tee -a $destin_dir/$logfile
done

echo " "
echo "----End $(date)---" | tee -a $destin_dir/$logfile

まず、ペアの配列が宣言され、各ペアは、置換文字列で、その後WHAT1のために置換されますFOR1OTHER_string_to replaceのために置換されますstring replacedファイルにFile.txt。ループでは配列が読み取られ、ペアの最初のメンバーはreplace_what=$iとして、2番目のメンバーはとして取得されreplace_for=$jます。このfindコマンドは、ディレクトリ内でファイル名(ワイルドカードを含む場合があります)を検索し、sed -i以前に定義されたものと同じファイル内で置き換えます。最後に、grepリダイレクトされたログファイルを追加して、ファイルに加えられた変更をログに記録しました。

これは私にとってGNU Bash 4.3 sed 4.2.2うまくいき、bashのタプルのループに対するVasyaNovikovの回答に基づいています 。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.