Bashコマンドからテキストファイル内を検索して置換する


561

検索を行うと、指定された入力文字列を置き換える、と言うための最も簡単な方法は何だabcと言うと、別の文字列に置き換えXYZたファイルには/tmp/file.txt

私はアプリを作成していて、IronPythonを使用してSSH経由でコマンドを実行しています。ただし、Unixについてはよく知らないので、何を探すべきかわかりません。

Bashは、コマンドラインインターフェイスであるだけでなく、非常に強力なスクリプト言語になる可能性があると聞きました。したがって、これが本当であれば、このようなアクションを実行できると思います。

bashでそれを行うことはできますか、そして私の目標を達成するための最も簡単な(1行の)スクリプトは何ですか?

回答:


930

最も簡単な方法は、sed(またはperl)を使用することです。

sed -i -e 's/abc/XYZ/g' /tmp/file.txt

-iオプションにより、sedを呼び出してインプレース編集を実行します。これはbashから呼び出すことができます。

本当に本当にbashだけを使いたいのであれば、以下がうまくいくでしょう:

while read a; do
    echo ${a//abc/XYZ}
done < /tmp/file.txt > /tmp/file.txt.t
mv /tmp/file.txt{.t,}

これは、各行をループし、置換を行い、一時ファイルに書き込みます(入力を破壊しないでください)。最後の移動は一時的に元の名前に移動するだけです。


3
ただし、mvの呼び出しは、sedを使用するのとほぼ同じように「非Bash」です。私はほとんど同じことをエコーと言いましたが、それは組み込みのシェルです。
スリム

5
ただし、Solarisにはsedの-i引数はありません(他の実装もあると思います)ので、注意してください。それを理解するのに数分を費やしただけです...
パンキー

2
自分への注意sed:: s/..../..../ - Substituteおよび /g - Global
チェックサムの

89
invalid command code Cエラーが発生するMacユーザーへの注意...インプレース置換の場合、BSD sed-i指定された拡張子でバックアップファイルを保存するため、フラグの後にファイル拡張子が必要です。例えば:sed -i '.bak' 's/find/replace/' /file.txt あなたはそうのような空の文字列を使用してバックアップをスキップすることができますsed -i '' 's/find/replace/' /file.txt
オースティン

8
ヒント:大文字と小文字を区別しない交換が必要な場合s/abc/XYZ/gi
Boris D. Teoharov '

166

ファイル操作は通常Bashではなく、Bashによって呼び出されたプログラムによって行われます。例:

perl -pi -e 's/abc/XYZ/g' /tmp/file.txt

-iフラグは、インプレースの交換を行うためにそれを伝えます。

man perlrun元のファイルのバックアップを取る方法など、詳細については、を参照してください。


37
私の純粋主義者は、Perlがシステムで利用可能になるかどうか確信が持てないと言っています。しかし、それが最近のケースであることはめったにありません。おそらく私は私の年齢を示しています。
スリムな

3
より複雑な例を示してください。「chdir / blah」を「chdir / blah2」に置き換えるようなもの。私が試しましたがperl -pi -e 's/chdir (?:\\/[\\w\\.\\-]+)+/chdir blah/g' text、パターンと次の単語の間にスペースがないとエラーが発生し続けます-e行1で非推奨になりました?:\\ / -e line 1
CMCDragonkai 2013年

@CMCDragonkaiこの回答をチェックしてください:stackoverflow.com/a/12061491/2730528
Alfonso Santiago

69

これにつまずいたときびっくりした...

パッケージにreplace付属しているコマンドがある"mysql-server"ので、インストールしている場合は試してください。

# replace string abc to XYZ in files
replace "abc" "XYZ" -- file.txt file2.txt file3.txt

# or pipe an echo to replace
echo "abcdef" |replace "abc" "XYZ"

詳細についてはman replace、を参照してください。


12
ここで2つのことが可能です:a)replace独立した便利なツールであり、MySQLの人々はそれを個別にリリースしてそれに依存replaceする必要がありますb)少しのMySQLが必要ですo_Oどちらの方法でも、mysql-serverをインストールして置き換える:)
フィリップホワイトハウス2014

Macでのみ機能しますか?私のubuntuでは私はそのコマンドが存在しないことを中心にしています
ポール

1
これは、mysql-serverパッケージがインストールされていないためです。@rayroが指摘しているように、そのreplace一部です。
Phius

2
「警告:replaceは非推奨であり、将来のバージョンで削除される予定です。」
Steven Vachon

1
WindowsでREPLACEコマンドを実行しないように注意してください!Windowsでは、REPLACEコマンドはファイルの高速複製用です。この議論には関係ありません。
Maor

53

これは古い投稿ですが、@ centurianが単一引用符は何も拡張されないことを意味するので、変数を使用したい人にとってはそうです。

変数を取得する簡単な方法は、文字列の連結を行うことです。これは、bashの並列によって行われるため、次のように機能するはずです。

sed -i -e "s/$var1/$var2/g" /tmp/file.txt

39

Bashは、他のシェルと同様に、他のコマンドを調整するためのツールにすぎません。通常、標準のUNIXコマンドを使用しようとしますが、もちろんBashを使用して、独自のコンパイル済みプログラム、他のシェルスクリプト、PythonおよびPerlスクリプトなどを含むすべてのものを呼び出すことができます。

この場合、それを行うにはいくつかの方法があります。

ファイルを読み取り、別のファイルに書き込む場合は、移動しながら検索/置換を行い、sedを使用します。

sed 's/abc/XYZ/g' <infile >outfile

ファイルをインプレースで編集する場合(エディターでファイルを開いて編集し、保存するかのように)、行エディター 'ex'に指示を入力します。

echo "%s/abc/XYZ/g
w
q
" | ex file

Exは全画面モードのないviに似ています。viの ':'プロンプトと同じコマンドを指定できます。


33

私はとりわけこのスレッドを見つけましたが、最も完全な回答が含まれていることに同意するので、私も追加します。

  1. sedそしてedその便利な...手です。@ジョニーからこのコードを見てください:

    sed -i -e 's/abc/XYZ/g' /tmp/file.txt
  2. 私の制限がシェルスクリプトでそれを使用することである場合、「abc」または「XYZ」の代わりに変数を内部で使用することはできません。BashFAQは、私は、少なくとも理解して何に同意しているようです。だから、私は使用できません:

    x='abc'
    y='XYZ'
    sed -i -e 's/$x/$y/g' /tmp/file.txt
    #or,
    sed -i -e "s/$x/$y/g" /tmp/file.txt
    

    しかし、何ができるでしょうか?@Johnnyが使用することを言ったように、while read...しかし、残念ながらそれは話の終わりではありません。以下は私とうまくいきました:

    #edit user's virtual domain
    result=
    #if nullglob is set then, unset it temporarily
    is_nullglob=$( shopt -s | egrep -i '*nullglob' )
    if [[ is_nullglob ]]; then
       shopt -u nullglob
    fi
    while IFS= read -r line; do
       line="${line//'<servername>'/$server}"
       line="${line//'<serveralias>'/$alias}"
       line="${line//'<user>'/$user}"
       line="${line//'<group>'/$group}"
       result="$result""$line"'\n'
    done < $tmp
    echo -e $result > $tmp
    #if nullglob was set then, re-enable it
    if [[ is_nullglob ]]; then
       shopt -s nullglob
    fi
    #move user's virtual domain to Apache 2 domain directory
    ......
    
  3. nullglobが設定されているかどうかを確認できるように、次のようにaを含む文字列があると、奇妙な動作*をします。

    <VirtualHost *:80>
     ServerName www.example.com
    

    なる

    <VirtualHost ServerName www.example.com

    終了山かっこはなく、Apache2もロードできません。

  4. この種類の解析は、1ヒットの検索と置換よりも遅くなるはずですが、すでに説明したように、1つの解析サイクルで機能する4つの異なる検索パターンに対して4つの変数があります。

問題の想定を前提に考えられる最も適切なソリューション。


12
あなたの(2)では-あなたがすることができsed -e "s/$x/$y/"、それはうまくいきます。二重引用符ではありません。変数自体の文字列に特別な意味を持つ文字が含まれている場合、深刻な混乱を招く可能性があります。たとえば、x = "/"またはx = "\"の場合。これらの問題が発生した場合、おそらく、このジョブにシェルを使用するのをやめるべきであることを意味します。
スリムな

こんにちは。Perlの使用にも反対しているようですね。あなたの解決策は何ですか?実際には、ファイル内のパスを動的に変更したいので、文字列に/がたくさんあります!
Mahdi、

20

使用できますsed

sed -i 's/abc/XYZ/gi' /tmp/file.txt

-i検索するテキストがabcor ABCやor AbCなどであるかわからない場合は、「大文字と小文字を区別しない」に使用します。

あなたは使用することができますfindし、sedあなたがあなたのファイル名がわからない場合は:

find ./ -type f -exec sed -i 's/abc/XYZ/gi' {} \;

すべてのPythonファイルを検索して置き換えます。

find ./ -iname "*.py" -type f -exec sed -i 's/abc/XYZ/gi' {} \;

12

edコマンドを使用して、ファイル内検索と置換を行うこともできます。

# delete all lines matching foobar 
ed -s test.txt <<< $'g/foobar/d\nw' 

詳細については、「スクリプトによるファイルの編集ed」を参照してください。


3
このソリューションは、GNU / FreeBSD(Mac OSX)の非互換性(とは異なりますsed -i <pattern> <filename>)から独立しています。非常に素晴らしい!
Peterino、2014

11

作業しているファイルがそれほど大きくなく、一時的に変数に格納しても問題がない場合は、ファイル全体に対して一度にBash文字列置換を使用できます。ファイルを1行ずつ調べる必要はありません。

file_contents=$(</tmp/file.txt)
echo "${file_contents//abc/XYZ}" > /tmp/file.txt

ファイルの内容全体は、改行を含む1つの長い文字列として扱われます。

XYZは変数など$replacementにすることができます。ここでsedを使用しない利点の1つは、検索または置換文字列にsedパターンの区切り文字(通常は、/とは限りません)が含まれていることを心配する必要がないことです。欠点は、正規表現やsedのより高度な操作を使用できないことです。


これをタブ文字で使用するためのヒントはありますか?何らかの理由で、私のスクリプトは、大量のエスケープを伴うsedからこのメソッドに変更した後、タブのあるものを何も見つけません。
Brian Hannay 2017

2
置き換える文字列にタブを配置する場合は、Bashの「ドル記号付き単一引用符」構文を使用して行うことができるため、タブは$ '\ t'で表され、$ echo 'tab' $を実行できます。 '\ t''separated'> testfile; $ file_contents = $(<testfile); $ echo "$ {file_contents // $ '\ t' / TAB}"; tabTABseparated `
johnraff


5

次のシェルコマンドを試してください。

find ./  -type f -name "file*.txt" | xargs sed -i -e 's/abc/xyz/g'

4
これは「誤ってすべてのサブディレクトリにあるすべてのファイルをどうやって処理するのか」に対する優れた答えですが、ここで尋ねられているようには見えません。
tripleee 2016年

この構文はのBSDバージョンでは機能しません。代わりsedに使用sed -i''してください。
kenorb

5

ファイル内のテキストを非対話的に編集するには、vimなどのインプレイステキストエディターが必要です。

コマンドラインから使用する簡単な例を次に示します。

vim -esnc '%s/foo/bar/g|:wq' file.txt

これは、基本的に同じものであるexエディターの@slim回答に相当します。

ここにいくつかのex実用的な例があります。

ファイル内のでテキストfooを置き換えるbar

ex -s +%s/foo/bar/ge -cwq file.txt

複数のファイルの末尾の空白を削除する:

ex +'bufdo!%s/\s\+$//e' -cxa *.txt

トラブルシューティング(端末が動かなくなった場合):

  • -V1paramを追加して、詳細なメッセージを表示します。
  • 強制終了:-cwq!

以下も参照してください。


インタラクティブに交換をしたかった。したがって、「vim -esnc '%s / foo / bar / gc |:wq' file.txt」を試しました。しかし、ターミナルは現在スタックしています。bashシェルが奇妙に動作することなく、どのようにインタラクティブに置換を行うか。
vineeshvs

デバッグ、追加-V1、強制終了するには、を使用しますwq!
kenorb

2

bashスクリプト内でもpythonを使用できます。私はここでのトップの回答のいくつかであまり成功していませんでしたが、ループを必要とせずにこれが機能することがわかりました:

#!/bin/bash
python
filetosearch = '/home/ubuntu/ip_table.txt'
texttoreplace = 'tcp443'
texttoinsert = 'udp1194'

s = open(filetosearch).read()
s = s.replace(texttoreplace, texttoinsert)
f = open(filetosearch, 'w')
f.write(s)
f.close()
quit()

1

rplコマンドを使用できます。たとえば、phpプロジェクト全体でドメイン名を変更するとします。

rpl -ivRpd -x'.php' 'old.domain.name' 'new.domain.name' ./path_to_your_project_folder/  

これは原因の明確なバッシュではありませんが、非常に迅速で便利です。:)

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