シェルスクリプトで1つの部分文字列を別の文字列に置き換えます


823

私は「スージーと結婚が大好き」で、「スージー」を「サラ」に変えたいです。

#!/bin/bash
firstString="I love Suzi and Marry"
secondString="Sara"
# do something...

結果は次のようになります。

firstString="I love Sara and Marry"

18
まず、スージーを穏やかに落とすことから始めます。:)
コードスニファー

それはあなたではなく、私です!それはスージーと話すためのひもであるはず
でした

回答:


1360

最初に出現するパターンを特定の文字列に置き換えるには、次のコマンドを使用します。${parameter/pattern/string}

#!/bin/bash
firstString="I love Suzi and Marry"
secondString="Sara"
echo "${firstString/Suzi/$secondString}"    
# prints 'I love Sara and Marry'

すべての出現箇所を置き換えるに、次を使用します。${parameter//pattern/string}

message='The secret code is 12345'
echo "${message//[0-9]/X}"           
# prints 'The secret code is XXXXX'

(これは、に記載されてバッシュリファレンスマニュアル、§3.5.3「シェルパラメータ展開」。)

この機能はPOSIXでは指定されていないことに注意してください。これはBash拡張機能であるため、すべてのUnixシェルがこの機能を実装しているわけではありません。関連するPOSIXドキュメントについては、The Open Group Technical Standard Base Specifications、Issue 7Shell&Utilitiesボリューム、§2.6.2 "Parameter Expansion"を参照してください


3
@ruakh orステートメントを使用してこのステートメントを記述する方法 SuziまたはMarryを新しい文字列に置き換えたい場合に限ります。
Priyatham51 2014年

3
@ Priyatham51:そのための組み込み機能はありません。1つを交換してから、もう1つを交換してください。
ruakh 2014年

4
@Bu:いいえ。\nそのコンテキストでは、改行ではなく、それ自体を表すためです。現在、Bashをテストするのに便利ではありませんが、のようなものを書くことができるはず$STRING="${STRING/$'\n'/<br />}"です。(たぶんあなたは望むSTRING//-代わりに全部を置き換える- STRING/
ruakh

25
明確にするために、これは私を少し混乱させたので、最初の部分は変数参照でなければなりません。あなたはできませんecho ${my string foo/foo/bar}。必要なものinput="my string foo"; echo ${input/foo/bar}
Henrik N

2
@mgutt:この回答は関連ドキュメントにリンクしています。ドキュメントは完全ではありません。たとえば、//直接言及するのではなく、「パターンが ' /'で始まる場合」に言及します(これは正確ではありません。その文の残りの/部分では、エクストラは実際にはパターン)-しかし、私はもっと良い情報源を知りません。
ruakh

208

これは、bash文字列操作で完全に実行できます。

first="I love Suzy and Mary"
second="Sara"
first=${first/Suzy/$second}

これにより、最初の出現のみが置き換えられます。それらをすべて置き換えるには、最初のスラッシュを2倍にします。

first="Suzy, Suzy, Suzy"
second="Sara"
first=${first//Suzy/$second}
# first is now "Sara, Sara, Sara"

9
グローバル置換オプションを使用して承認済みの回答を編集するのではなく、承認済みの回答を複製することの意味は何ですか?そして、正規表現がサポートされている間、それを追加します。そして、「悪い置換」で何が起こっているのかを説明します。十分なポイントがあります、@ Kevin :)
Dan Dascalescu

6
彼らは両方とも正確に同じ分で答えたようです:O
Jamie Hutber

76

Dashの場合、以前の投稿はすべて機能しません

POSIX sh互換ソリューションは次のとおりです。

result=$(echo "$firstString" | sed "s/Suzi/$secondString/")

1
私はこれを手に入れました:$ echo $(echo $ firstString | sed 's / Suzi / $ secondString / g') $ secondString and Marry
Qstnr_La

2
変数の置換のための二重引用符を使用@Qstnr_La:result=$(echo $firstString | sed "s/Suzi/$secondString/g")
EMC

1
変数への出力方法も示す1。ありがとう!
ファラオシェフ、

2
一重引用符を修正し、echo引数の周りに欠落している引用符も追加しました。それは単純な文字列で引用することなく一見機能しますが、重要な入力文字列(不規則な間隔、シェルのメタ文字など)で簡単に壊れます。
Tripleee

sh(AWS Codebuild / Ubuntu sh)では、最後に2つのスラッシュではなく、1つのスラッシュが必要であることがわかりました。上記のコメントにも単一のスラッシュが表示されているので、コメントを編集します。
ティム

67

これを試して:

 sed "s/Suzi/$secondString/g" <<<"$firstString"

19
これには実際にSedは必要ありません。Bashはこの種の置換をネイティブでサポートしています。
ruakh

12
これは「bash」というタグが付いていると思いますが、別のシェルに簡単なものが必要だったため、ここに来ました。これは、wiki.ubuntu.com /…の代わりに使用できる簡潔な方法です。
natevw 2014

3
これは、ash / dashまたはその他のPOSIX shに最適です。
Yoshua Wuyts 2015年

2
エラーsed
Nam G VU

1
文字列のパイプライン処理に最適なstdinで動作する最初の回答。
Dinei

46

sed文字列にRegExp文字が含まれている場合よりもbashを使用する方が適切です。

echo ${first_string/Suzi/$second_string}

Windowsに移植可能で、少なくともBash 3.1と同じくらい古いバージョンで動作します。

エスケープについてあまり心配する必要がないことを示すために、これを有効にしましょう:

/home/name/foo/bar

これに:

~/foo/bar

しかし/home/name、最初にある場合にのみ。必要ありませんsed

bashが魔法の変数$PWDand を与えるとすると$HOME、次のことができます。

echo "${PWD/#$HOME/\~}"

編集:引用/エスケープに関するメモのコメントでMark Haferkampに感謝し~ます。*

変数$HOMEにスラッシュがどのように含まれているのかに注意してください。

参考資料Advanced Bash-Scripting Guide
を使用sedする必要がある場合は、必ずすべての文字エスケープしてください


この回答により、カスタム変数を実行するたびに新しい変数を定義することを避けるためにsedpwdコマンドを使用できなくなりました$PS1。Bashは、マジック変数よりも一般的な方法で、コマンドの出力を文字列置換に使用できますか?あなたのコードについては、~Bashがそれを$ HOMEに展開しないように、をエスケープする必要がありました。また、#コマンドのは何をしますか?
Mark Haferkamp、2015年

1
@MarkHaferkamp 「さらに読むことをお勧めします」リンクからこれ参照してください。「エスケープ~」について:どのように引用したかに注意してください。常に引用することを忘れないでください!そして、これは魔法の変数だけでは機能しません。どの変数も、bash内で置換、文字列長の取得などが可能です。高速化を試みて、おめでとうございます。別のプログラミング言語に慣れていて、コンパイルされたプロンプトをコーディングしたい$PS1場合にも興味があるかもしれ$PROMPT_COMMANDません。
Camilo Martin

「さらに読む」リンクは「#」について説明しています。バッシュ4.3.30で、echo "${PWD/#$HOME/~}"私に代わるものではありません$HOME~。交換~\~たり'~'作品。これらはいずれも、別のディストリビューションのBash 4.2.53で動作します。~互換性を高めるために、を引用またはエスケープするように投稿を更新できますか?「マジック変数」の質問で私が意味したunameことは、最初に変数として保存せずに、出力などでBashの変数置換を使用できるかどうかです。私の個人的なことは$PROMPT_COMMAND複雑です。
Mark Haferkamp、2015年

@MarkHaferkampおっと、あなたは完全に正しいです。今答えを更新します。
カミロマーティン

1
@MarkHaferkamp Bashとそのあいまいな落とし穴...:P
Camilo Martin

19

明日あなたが結婚を愛さないと決めた場合、彼女も同様に置き換えることができます:

today=$(</tmp/lovers.txt)
tomorrow="${today//Suzi/Sara}"
echo "${tomorrow//Marry/Jesica}" > /tmp/lovers.txt

あなたの恋人を去る50の方法がなければなりません。


9

コメントをつけられないので。@ruaka例を読みやすくするには、次のように記述します

full_string="I love Suzy and Mary"
search_string="Suzy"
replace_string="Sara"
my_string=${full_string/$search_string/$replace_string}
or
my_string=${full_string/Suzy/Sarah}

私があなたの例に来るまで、私は逆に注文を理解していました。これは何が起こっているのかを明確にするのに役立ちました
raghav710

5

純粋なPOSIXシェルメソッド。これは、Roman Kazanovskyisedベースの回答とは異なり、外部ツールを必要とせず、シェル自体のネイティブパラメーター拡張のみが必要です。コードが1行に収まるように、長いファイル名は最小化されていることに注意してください。

f="I love Suzi and Marry"
s=Sara
t=Suzi
[ "${f%$t*}" != "$f" ] && f="${f%$t*}$s${f#*$t}"
echo "$f"

出力:

I love Sara and Marry

使い方:

  • 最小のサフィックスパターンを削除します。 "${f%$t*}"リターン「I love」サフィックスがあれば$tSuzi*」です$f「」。 I love Suzi and Marry

  • しかしの場合t=Zelda"${f%$t*}"何も削除せず、文字列「I love Suzi and Marry」全体を返します。

  • これがあれば、テストに使用され$tている$f[ "${f%$t*}" != "$f" ]まで評価される真の場合$f、文字列が含まれています「Suzi*」と虚偽の場合ではありません。

  • テストが戻る場合は、使用して目的の文字列を構築削除最小のサフィックス・パターンを ${f%$t*}I love」と最小プレフィックスパターンを削除する ${f#*$t}and Marryと、」第二の文字列$sSaraの間で」。


5

私はこれが古いことを知っていますが、awkの使用について誰も言及していないので:

    firstString="I love Suzi and Marry"
    echo $firstString | awk '{gsub("Suzi","Sara"); print}'

1
そのトリックは、分割しようとしているものが実際には変数ではなくテキストファイルにある場合の方法です。ありがとう、@ Payam!
フェリペSSシュナイダー

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