Bash'deki bir dizeden sabit bir öneki / soneki kaldırma


485

Benim içinde bashkomut dosyası bir dize ve onun ön / son ek var. Orijinal dizeden önek / sonek kaldırmak gerekir.

Örneğin, aşağıdaki değerlere sahip olduğumu varsayalım:

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

Aşağıdaki sonuca nasıl ulaşabilirim?

result="o-wor"


14
Gelişmiş Bash Komut Dosyası Kılavuzu'na bağlanırken çok dikkatli olun; iyi tavsiye ve korkunç bir karışımı içerir.
Üçlü

Yanıtlar:


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

40
Ayrıca $ # öneki veya $ soneki joker karakterler içeriyorsa mümkün olduğunca kaldıran ## ve %% vardır.
puan

28
İkisini tek bir satırda birleştirmenin bir yolu var mı? Denedim ${${string#prefix}%suffix}ama işe yaramıyor.
static_rtti

28
@static_rtti Hayır, ne yazık ki parametre değiştirmeyi bu şekilde iç içe yerleştiremezsiniz. Biliyorum, bu bir utanç.
Adrian Frühwirth

87
@ AdrianFrühwirth: tüm dil bir utanç, ama çok yararlı :)
static_rtti

8
Nvm, Google'da "bash yerine koyma" istediğimi buldu.
Tyler

89

Sed kullanma:

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

Sed komutunda ^karakter $prefix, ile başlayan metni ve sondaki $ile biten metni eşleştirir $suffix.

Adrian Frühwirth, aşağıdaki yorumlarda bazı iyi noktalara değinmektedir, ancak sedbu amaçla çok yararlı olabilir. $ Öneki ve $ soneki içeriğinin sed tarafından yorumlanması gerçeği iyi VEYA kötü olabilir - dikkat ettiğiniz sürece iyi olmalısınız. Güzellik şu ki, böyle bir şey yapabilirsiniz:

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

Bu da istediğiniz gibi olabilir ve bash değişken ikamesinden daha meraklı ve daha güçlüdür. Büyük bir güçle (Spiderman'ın dediği gibi) büyük sorumluluk getirdiğini hatırlarsanız, iyi olmalısınız.

Sed'e hızlı bir giriş http://evc-cit.info/cit052/sed_tutorial.html adresinde bulunabilir.

Kabuk ve dizeleri kullanımı ile ilgili bir not:

Verilen belirli örnek için aşağıdakiler de işe yarar:

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

... ama sadece:

  1. echo argüman listesinde kaç tane dizenin olduğu umurumda değil ve
  2. $ Önekinde ve $ sonekinde boşluk yok

Komut satırında bir dize alıntı yapmak genellikle iyi bir uygulamadır, çünkü boşluklar olsa bile komuta tek bir argüman olarak sunulacaktır. Aynı sebepten dolayı $ öneki ve $ son ekini sunuyoruz: sed'e yapılan her düzenleme komutu bir dize olarak geçirilecektir. Değişken enterpolasyona izin verdikleri için çift tırnak kullanırız; tek tırnak kullansaydık sed komutunun değişmez bir değeri vardı $prefixve $suffixbu kesinlikle istediğimiz şey değildi.

Bildirim da tek tırnak kullanımım değişkenleri ayarlarken prefixve suffix. Dizelerde hiçbir şeyin yorumlanmasını kesinlikle istemiyoruz, bu yüzden bunları tek tek alıntılıyoruz, böylece hiçbir enterpolasyon gerçekleşmiyor. Yine, bu örnekte gerekli olmayabilir, ancak içine girmek çok iyi bir alışkanlıktır.


8
Ne yazık ki, bu çeşitli nedenlerle kötü bir tavsiye: 1) Alıntısız, $stringkelime bölme ve globbing tabi. 2) $prefixve yorumlayacak $suffixifadeler içerebilir sed, örneğin normal ifadeler veya tüm komutu kıracak sınırlayıcı olarak kullanılan karakter. 3) sedİki kez aramak gerekli değildir ( -e 's///' -e '///'bunun yerine) ve borudan da kaçınılabilir. Örneğin, göz önünde string='./ *'ve / veya prefix='./'ve korkunç nedeniyle kırmaya bkz 1)ve 2).
Adrian Frühwirth

Eğlenceli not: sed sınırlayıcı olarak neredeyse her şeyi alabilir. Benim durumumda, ben yolların dışında önek dizinleri ayrıştırma beri, ben kullanamadı /kullandığım bu yüzden, sed "s#^$prefix##bunun yerine,. (Kırılganlık: dosya adları içeremez #. Dosyaları kontrol ettiğim için orada güvendeyiz.)
Olie

@Olie Dosya adları eğik çizgi ve boş karakter dışında herhangi bir karakter içerebilir , bu nedenle kontrol sizde değilse belirli dosya içermeyen dosya adını varsayamazsınız .
Adrian Frühwirth

Evet, orada ne düşündüğümü bilmiyorum. iOS belki? Dunno. Dosya adları kesinlikle "#" içerebilir. Bunu neden söylediğim hakkında hiçbir fikrim yok. :)
Olie

@Olie: Orijinal yorumunuzu anladığım gibi, sed'in #sınırlayıcısı olarak kullanmayı seçtiğiniz sınırlamanın, bu karakteri içeren dosyaları işleyemeyeceğiniz anlamına geldiğini söylüyordunuz.
P Daddy

17

Ön ekinizin ve son ekinizin uzunluğunu biliyor musunuz? Senin durumunda:

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

Veya daha genel:

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

Ancak Adrian Frühwirth'in çözümü çok güzel! Bunu bilmiyordum!


14

(Bu tarafından iyi işlenmeyen) yollardan önek kaldırmak için grep kullanın sed:

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

\K maçtan önceki tüm karakterleri kaldırır.


grep -Pstandart olmayan bir uzantıdır. Platformunuzda destekleniyorsa daha fazla güç sağlar, ancak kodunuzun makul derecede taşınabilir olması gerekiyorsa bu şüpheli bir tavsiyedir.
üçlü

@tripleee Gerçekten. Ancak GNU Bash yüklü bir sistemde PCRE'yi destekleyen bir grep var.
Vladimir Petrakovich

1
Hayır, örneğin MacOS, Bash'i kutudan çıkarır, ancak GNU'ya sahip değildir grep. Daha önceki sürümlerde aslında -PBSD seçeneği vardı, grepancak kaldırdılar.
Üçlü

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

Notlar:

# $ prefix: # eklenmesi, "cehennem" alt dizesinin yalnızca başlangıçta bulunması durumunda kaldırılmasını sağlar. % $ son eki:% eklenmesi, "ld" alt dizesinin yalnızca sonunda bulunursa kaldırılmasını sağlar.

Bunlar olmadan, "cehennem" ve "ld" alt dizeleri, ortada bile olsa her yerde kaldırılır.


Notlar için teşekkürler! qq: kod örneğinizde /dizeden hemen sonra eğik çizgi var , bunun için ne var?
DiegoSalazar

1
/ geçerli dizeyi ve alt dizeyi ayırır. alt-string burada th gönderilen soru sonekidir.
Vijay Vat


6

Küçük ve evrensel çözüm:

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

1
Bash kullanıyorsanız, muhtemelen hiç kullanmamalısınız expr. It is a çeşit orijinal Bourne kabuğunun günlerde uygun mutfak lavabosu yarar arka fakat yolu, tarihten en önce geçmiş artık.
üçlü

5

@Adrian Frühwirth yanıtını kullanma:

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

böyle kullan

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

0

Regex'te yakalama gruplarından faydalanırdım:

$ 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})).)*)içeriğinin ${suffix}yakalama grubundan hariç tutulmasını sağlar. Örnek olarak, dize eşdeğerdir [^A-Z]*. Aksi takdirde:

$ perl -pe "s/${prefix}(.*)${suffix}/\1/g" <<< $string1
o-worldhello-wor
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.