Kabuk betiğindeki başka bir dize için bir alt dizeyi değiştirme


822

"Suzi ve Marry'i seviyorum" var ve "Suzi" yi "Sara" olarak değiştirmek istiyorum.

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

Sonuç şu şekilde olmalıdır:

firstString="I love Sara and Marry"

17
Suzi'yi nazikçe aşağı bırakarak başlardım. :)
codesniffer

Bu sen değil, benim! bu Suzi
GoodSp33d

Yanıtlar:


1359

Bir desenin ilk tekrarlamasını belirli bir dizeyle değiştirmek için şunu kullanın :${parameter/pattern/string}

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

Tüm oluşumları değiştirmek için şunu kullanın :${parameter//pattern/string}

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

(Bu belgelenmiştir Bash Reference Manual §3.5.3, "Kabuk Parametrelerinin" .)

Bu özelliğin POSIX tarafından belirtilmediğini unutmayın - bir Bash uzantısıdır - bu nedenle tüm Unix kabukları bunu uygulamaz. İlgili POSIX belgeleri için, bkz . Open Group Teknik Standart Temel Özellikleri, Sayı 7 , Shell & Utilities birimi, §2.6.2 "Parametre Genişletme" .


3
@ruakh bu ifadeyi bir veya koşuluyla nasıl yazarım. Suzi veya Marry'yi yeni bir dize ile değiştirmek istersem.
Priyatham51

3
@ Priyatham51: Bunun için yerleşik bir özellik yok. Sadece birini, sonra diğerini değiştirin.
ruakh

4
@Bu: Hayır, çünkü \nbu bağlamda bir satırsonu değil kendini temsil ederdi. Bash'i test etmek için şu anda kullanışlı değilim, ama böyle bir şey yazabilmelisin $STRING="${STRING/$'\n'/<br />}". (Muhtemelen isteseniz de STRING//- yerine tüm - yerineSTRING/
ruakh

25
Açık olmak gerekirse, bu beni biraz karıştırdığından, ilk bölüm değişken bir referans olmalı. Yapamazsın echo ${my string foo/foo/bar}. İhtiyacınız olacakinput="my string foo"; echo ${input/foo/bar}
Henrik N

2
@mgutt: Bu cevap ilgili belgelere bağlantı verir. Dokümantasyon mükemmel değil - örneğin, //doğrudan bahsetmektense , " örüntü ' /' ile başlıyorsa (bu doğru bile değil, çünkü bu cümlenin geri kalanı ekstraın /aslında bir parçası olmadığını varsayar . - ama daha iyi bir kaynak bilmiyorum.
ruakh

208

Bu tamamen bash string manipülasyonu ile yapılabilir:

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

Bu sadece ilk olayın yerini alacaktır; hepsini değiştirmek için ilk eğik çizgiyi iki katına çıkarın:

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

9
Genel değiştirme seçeneğiyle kabul edileni düzenlemek yerine, kabul edileni çoğaltan bir cevaba sahip olmanın anlamı nedir? Ayrıca, normal ifadelerin eklenmesi desteklenir. Ve "Kötü ikame" ile olanları açıklamak. Yeterli puanınız var, @Kevin :)
Dan Dascalescu

6
Her ikisinin de aynı dakika içinde cevap verdiği
anlaşılıyor

76

Dash için önceki tüm yayınlar çalışmıyor

POSIX shuyumlu çözüm:

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

1
Bunu aldım: $ echo $ (echo $ firstString | sed 's / Suzi / $ secondString / g') $ secondString ve Marry
Qstnr_La 9:17 '

2
@Qstnr_La değişken ikamesi için çift tırnak kullanır:result=$(echo $firstString | sed "s/Suzi/$secondString/g")
emc

1
Bir değişkene nasıl çıktı verileceğini gösteren artı 1. Teşekkürler!
Şef Firavun

2
Tek tırnakları düzelttim ve echoargümanın etrafındaki eksik tırnakları da ekledim . Basit dizelerle alıntı yapmadan aldatıcı bir şekilde çalışır, ancak önemsiz olmayan herhangi bir giriş dizesini (düzensiz aralık, kabuk metakarakterleri, vb.)
tripleee

Sh (AWS Codebuild / Ubuntu sh) sonunda bir çift değil, tek bir eğik çizgi ihtiyacım olduğunu buldum. Yukarıdaki yorumlar da tek bir eğik çizgi gösterdiğinden yorumu düzenleyeceğim.
Tim

67

bunu dene:

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

19
Bunun için aslında Sed'e ihtiyacınız yok; Bash bu tür değiştirmeyi doğal olarak destekler.
ruakh

12
Sanırım bu "bash" etiketli ama buraya geldi çünkü başka bir kabuk için basit bir şey gerekli. Bu, wiki.ubuntu.com/… 'ın ihtiyaç duyduğum gibi görünmesine ne güzel bir alternatif .
natevw

3
Bu kül / çizgi veya diğer POSIX sh için harika çalışıyor.
Yoshua Wuyts

2
Hata alıyorum sed: -e ifade # 1, karakter 9: bilinmeyen seçenek `s
Nam G VU

1
İlk cevap stdin ile çalışır, boru hatları için harika.
Dinei

46

sedDizeleri RegExp karakterlerine sahip olmaktan daha iyi bash kullanmak daha iyidir .

echo ${first_string/Suzi/$second_string}

Windows'a taşınabilir ve en azından Bash 3.1 kadar eski ile çalışır.

Kaçmak için endişelenmenize gerek olmadığını göstermek için şunu açalım:

/home/name/foo/bar

Bunun içine:

~/foo/bar

Ama sadece /home/namebaşlangıçta ise. İhtiyacımız yok sed!

Bash bize sihirli değişkenleri verdiğini göz önüne alındığında $PWDve $HOMEbiz yapabilir:

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

EDIT: Mark Haferkamp için alıntı / kaçış notu yorumlarında teşekkürler ~. *

Değişkenin $HOMEeğik çizgileri nasıl içerdiğine dikkat edin, ancak bu hiçbir şeyi bozmadı.

İlave değerler: Gelişmiş Bash-Scripting Kılavuzu .
Kullanmak şartsa, her karakterden kaçtığınızdansed emin olun .


Bu yanıt sed, pwdözel $PS1çalıştığımda her çalıştırıldığında yeni bir değişken tanımlamaktan kaçınmak için komutu kullanmamı engelledi. Bash, string değiştirme için bir komutun çıktısını kullanmak için sihirli değişkenlerden daha genel bir yol sunuyor mu? Kodunuza gelince, ~Bash'in $ HOME'a genişletilmesini önlemek için kaçmak zorunda kaldım . Ayrıca, #komutunuzdaki ne işe yarar?
Mark Haferkamp

1
@MarkHaferkamp bu Bkz bağlantıyı "ileri okuma tavsiye". Hakkında "kaçmak ~": nasıl şeyler alıntı dikkat edin. Her zaman bir şeyler teklif etmeyi unutmayın! Ve bu sadece sihirli değişkenler için işe yaramaz: herhangi bir değişken bash içinde ikame, dize uzunluğu alma ve daha fazlasını yapabilir. $PS1Hızlı çalıştığınız için tebrikler : $PROMPT_COMMANDbaşka bir programlama dilinde daha rahatsanız ve derlenmiş bir istemi kodlamak istiyorsanız da ilginizi çekebilir .
Camilo Martin

"Daha fazla okuma" bağlantısı "#" ı açıklar. On Bash 4.3.30, ile echo "${PWD/#$HOME/~}"değiştirmez benim . Değiştirme ile veya çalışır. Bunlardan herhangi biri başka bir dağıtımda Bash 4.2.53 üzerinde çalışır. Daha iyi uyumluluk için teklifinizi güncellemek veya kaçmak için lütfen gönderinizi güncelleyebilir misiniz ? Benim "sihirli değişkenler" sorumla kastettiğim şuydu: Bash'in değişken ikamesini, örneğin, önce değişken olarak kaydetmeden çıktısında kullanabilir miyim ? Kişisel gelince , bu karmaşık . $HOME~~\~'~'~uname$PROMPT_COMMAND
Mark Haferkamp

@MarkHaferkamp Whoa, tamamen haklısın, benim hatam. Cevabı şimdi güncelleyecek.
Camilo Martin

1
@MarkHaferkamp Bash ve belirsiz tuzakları ...: P
Camilo Martin

19

Yarın Marry'yi sevmediğinize karar verirseniz, o da değiştirilebilir:

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

Sevgilinizi terk etmenin 50 yolu olmalı.


9

Yorum ekleyemediğim için. @ruaka Örneği daha okunabilir hale getirmek için şöyle yazın

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}

Örneğinize gelene kadar emri ters yönde anlamıştım. Bu, neler olduğunu netleştirmeye yardımcı oldu
raghav710

5

Saf POSIX aksine kabuk yöntemi, Roma Kazanovskyi 'ın sedtabanlı cevap harici araçlara ihtiyacı, sadece kabuk kendi yerli parametre açılımları . Kodun bir satıra daha iyi uyması için uzun dosya adlarının en aza indirildiğini unutmayın:

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

Çıktı:

I love Sara and Marry

Nasıl çalışır:

  • En Küçük Sonek Desenini Kaldır. "${f%$t*}"döner " I love" eki ise $t" Suzi*" içindedir $f" ". I love Suzi and Marry

  • Ama eğer t=Zelda, o zaman "${f%$t*}"hiçbir şey silmez ve " I love Suzi and Marry" dizesinin tamamını döndürür .

  • Bu teste kullanılan $tiçindedir $file [ "${f%$t*}" != "$f" ]hangi değerlendirecek gerçek ise $fdize içerir "Suzi* " ve sahte değilse.

  • Test true değerini döndürürse , aradaki 2. " " dizesi ile " " En Küçük Sonek Desenini Kaldır ve ${f%$t*}" I love" En Küçük Önek Desenini Kaldır'ı kullanarak istediğiniz dizeyi oluşturun .${f#*$t}and Marry$sSara


5

Bu eski olduğunu biliyorum ama kimse awk kullanarak bahsetmedi çünkü:

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

1
Bu hile, bölmeye çalıştığınız şeyin aslında bir değişkende değil, bir metin dosyasında gitmesinin yoludur. Teşekkürler, @Payam!
Felipe SS Schneider

2
echo [string] | sed "s/[original]/[target]/g"
  • "s", "ikame" anlamına gelir
  • "g", "global, eşleşen tüm oluşumlar" anlamına gelir
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.