Bashの文字列から固定の接頭辞/接尾辞を削除する


485

私のbashスクリプトには、文字列とそのプレフィックス/サフィックスがあります。元の文字列からプレフィックス/サフィックスを削除する必要があります。

たとえば、次の値があるとします。

string="hello-world"
prefix="hell"
suffix="ld"

次の結果を取得するにはどうすればよいですか?

result="o-wor"


14
いわゆるAdvanced Bash Scripting Guideにリンクするときは注意してください。それは良いアドバイスとひどいの混合が含まれています。
tripleee 2016年

回答:


719
$ foo=${string#"$prefix"}
$ foo=${foo%"$suffix"}
$ echo "${foo}"
o-wor

40
##と%%もあります。これらは、$ prefixまたは$ suffixにワイルドカードが含まれている場合、可能な限り削除します。
2013年

28
2つを1行で組み合わせる方法はありますか?試しました${${string#prefix}%suffix}がうまくいきません。
static_rtti 14

28
@static_rttiいいえ、残念ながら、このようにパラメーター置換をネストすることはできません。私は知っている、それは残念だ。
エイドリアンFrühwirth14年

87
@AdrianFrühwirth:言語全体は残念ですが、とても便利です:)
static_rtti

8
Nvm、Googleの「bash置換」で、私が欲しかったものが見つかりました。
タイラー

89

sedの使用:

$ echo "$string" | sed -e "s/^$prefix//" -e "s/$suffix$//"
o-wor

sedコマンド内では、^文字はで始まるテキストに一致$prefixし、末尾はで$終わるテキストに一致します$suffix

AdrianFrühwirthは、以下のコメントでいくつかの良い点を述べていますがsed、この目的のために非常に役立ちます。$ prefixと$ suffixの内容がsedによって解釈されるという事実は、良い場合も悪い場合もあります。注意を払っている限り、問題はありません。美しさは、次のようなことができることです。

$ prefix='^.*ll'
$ suffix='ld$'
$ echo "$string" | sed -e "s/^$prefix//" -e "s/$suffix$//"
o-wor

これはあなたが望むものかもしれません、そしてbash変数置換よりもより洗練されていてより強力です。スパイダーマンが言うように、大きな力には大きな責任が伴うことを覚えているなら、大丈夫です。

sedの簡単な紹介は、http://evc-cit.info/cit052/sed_tutorial.htmlにあります。

シェルとその文字列の使用に関する注意:

与えられた特定の例では、以下も同様に機能します:

$ echo $string | sed -e s/^$prefix// -e s/$suffix$//

...しかし理由は次のとおりです:

  1. echoは引数リストにある文字列の数を気にしません。
  2. $ prefixと$ suffixにスペースはありません

文字列にスペースが含まれている場合でも、単一の引数としてコマンドに表示されるため、コマンドラインで文字列を引用符で囲むことは、一般的には良い習慣です。同じ理由で$ prefixと$ suffixを引用しています:sedへの各編集コマンドは1つの文字列として渡されます。変数の補間が可能なため、二重引用符を使用しています。単一引用符を使用した場合、sedコマンドはリテラル$prefixを取得することになり、$suffixこれは確かに私たちが望んでいたものではありません。

また、変数prefixとを設定するときに一重引用符を使用していることに注意してくださいsuffix。文字列の何も解釈されないようにしたいので、それらを単一引用符で囲み、補間は行われません。繰り返しになりますが、この例では必要ないかもしれませんが、始めるのはとても良い習慣です。


8
残念ながら、これはいくつかの理由で悪いアドバイスです:1)引用符で囲まれていない場合、$string単語分割とグロブの影響を受けます。2)$prefixそして、解釈する$suffix表現を含めることができますsed。たとえば、正規表現や、コマンド全体を壊す区切り文字として使用される文字。3)sed2回の呼び出しは必要ありません(-e 's///' -e '///'代わりに行うことができます)。パイプも回避できます。たとえば、考えるstring='./ *'および/またはprefix='./'、それが原因に恐ろしく破る見る1)2)
エイドリアンFrühwirth2014年

楽しいメモ:sedは区切り文字としてほとんど何でも取ることができます。私の場合、プレフィックスディレクトリをパスから解析していたため/、を使用できなかったためsed "s#^$prefix##、代わりにを使用しました。(脆弱性:ファイル名にを含めることはできません#。私がファイルを制御しているので、安全です。)
Olie

@Olieファイル名には、スラッシュとヌル文字以外の任意の文字を含めることができるため、管理者でない限り、ファイル名に特定の文字が含まれていないとは想定できません。
AdrianFrühwirth、2015

ええ、私がそこで何を考えていたかわからない。iOS多分?ダンノ。ファイル名には必ず「#」を含めることができます。なぜ私がそれを言ったのか分かりません。:)
Olie

@オリー:私はあなたの元のコメントを理解したように#、sedの区切り文字として使用する選択の制限は、その文字を含むファイルを処理できないことを意味すると言っていました。
Pダディ

17

接頭辞と接尾辞の長さを知っていますか?あなたの場合:

result=$(echo $string | cut -c5- | rev | cut -c3- | rev)

またはより一般的な:

result=$(echo $string | cut -c$((${#prefix}+1))- | rev | cut -c$((${#suffix}+1))- | rev)

しかし、AdrianFrühwirth解決策は非常に優れています。知らなかった!


14

パスからプレフィックスを削除するためにgrepを使用します(これはによって適切に処理されませんsed)。

echo "$input" | grep -oP "^$prefix\K.*"

\K その前のすべての文字を一致から削除します。


grep -P非標準の拡張です。お使いのプラットフォームでサポートされていれば、より強力になりますが、コードを適切に移植できるようにする必要がある場合、これは疑わしいアドバイスです。
Tripleee

@tripleee確かに。しかし、GNU BashがインストールされているシステムにもPCREをサポートするgrepがあると思います。
Vladimir Petrakovich

1
いいえ、たとえばMacOSにはそのままのBashがありますが、GNUはありませんgrep。以前のバージョンには実際には-PBSDからのオプションgrepがありましたが、削除されました。
Tripleee

9
$ string="hello-world"
$ prefix="hell"
$ suffix="ld"

$ #remove "hell" from "hello-world" if "hell" is found at the beginning.
$ prefix_removed_string=${string/#$prefix}

$ #remove "ld" from "o-world" if "ld" is found at the end.
$ suffix_removed_String=${prefix_removed_string/%$suffix}
$ echo $suffix_removed_String
o-wor

ノート:

#$ prefix:追加#は、部分文字列「hell」が最初に見つかった場合にのみ削除されるようにします。%$ suffix:%を追加すると、部分文字列 "ld"が最後に見つかった場合にのみ削除されるようになります。

これらがなければ、部分文字列「hell」と「ld」は、途中で見つかったとしても、どこでも削除されます。


メモをありがとう!qq:コード例では/、文字列の直後にもスラッシュがありますが、それは何のためですか?
DiegoSalazar

1
/は、現在の文字列とサブ文字列を区切ります。ここのサブストリングは、投稿された質問のサフィックスです。
Vijay Vat


6

小さくて普遍的なソリューション:

expr "$string" : "$prefix\(.*\)$suffix"

1
Bashを使用している場合は、おそらくまったく使用exprしないでください。これは、元のボーンシェルの時代に便利なキッチンシンクユーティリティのようなものでしたが、現在は賞味期限を大幅に超えています。
tripleee

5

@AdrianFrühwirthの回答を使用します。

function strip {
    local STRING=${1#$"$2"}
    echo ${STRING%$"$2"}
}

このように使います

HELLO=":hello:"
HELLO=$(strip "$HELLO" ":")
echo $HELLO # hello

0

私は正規表現でキャプチャグループを利用します。

$ string="hello-world"
$ prefix="hell"
$ suffix="ld"
$ set +H # Disables history substitution, can be omitted in scripts.
$ perl -pe "s/${prefix}((?:(?!(${suffix})).)*)${suffix}/\1/" <<< $string
o-wor
$ string1=$string$string
$ perl -pe "s/${prefix}((?:(?!(${suffix})).)*)${suffix}/\1/g" <<< $string1
o-woro-wor

((?:(?!(${suffix})).)*)のコンテンツが${suffix}キャプチャグループから除外されることを確認します。例としては、と同等の文字列です[^A-Z]*。そうでなければあなたは得るでしょう:

$ perl -pe "s/${prefix}(.*)${suffix}/\1/g" <<< $string1
o-worldhello-wor
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.