Sed yerine desen için bir dizeden kaç


317

Benim bash betiğimde sed deseninde kullanmam gereken harici (kullanıcıdan alınan) bir dize var.

REPLACE="<funny characters here>"
sed "s/KEYWORD/$REPLACE/g"

Değişmez bir yedek olarak $REPLACEgüvenli bir şekilde kabul edilmek için dizeden nasıl çıkabilirim sed?

NOT:KEYWORD Hiçbir maçları vb kullanıcı tarafından sağlanmazsa bir aptal alt dize olduğunu.


13
"/ G -e 's / PASSWORD =. * / PASSWORD = abc / g'" diyorlarsa "Küçük Bobby Tablolar" sorununu önlemeye mi çalışıyorsunuz?
Paul Tomblin

2
Eğer bash kullanıyorsanız, sed'e ihtiyacınız yoktur. Just useoutputvar="${inputvar//"$txt2replace"/"$txt2replacewith"}".
destenson

@destenson Bence iki değişkeni tırnakların dışına koymamalısınız. Bash, çift tırnak içindeki değişkenleri okuyabilir (örneğin, boşluk boşlukları bozabilir).
Camilo Martin


1
@CamiloMartin, kendi cevabımdaki yorumuma bakın. $ {} İçindeki alıntılar içindeki alıntılarla eşleşmiyor. İki değişken tırnak işaretleri dışında değildir .
destenson

Yanıtlar:


268

Uyarı : Bu mu değil yeni satırlar düşünün. Daha ayrıntılı bir yanıt için bunun yerine bu SO sorusuna bakın . (Teşekkürler, Ed Morton ve Niklas Peter)

Her şeyden kaçmanın kötü bir fikir olduğunu unutmayın. Sed , özel anlamlarını elde etmek için kaçmak için birçok karaktere ihtiyaç duyar . Örneğin, değiştirme dizesindeki bir basamaktan kaçarsanız, bir geri başvuruya dönüşür.

Ben Blank'in dediği gibi, yedek dizede kaçması gereken sadece üç karakter var (kendiliğinden kaçıyor, ifadenin sonu ve & tümünü değiştir için eğik çizgi):

ESCAPED_REPLACE=$(printf '%s\n' "$REPLACE" | sed -e 's/[\/&]/\\&/g')
# Now you can use ESCAPED_REPLACE in the original sed statement
sed "s/KEYWORD/$ESCAPED_REPLACE/g"

Eğer KEYWORDdizeden kaçmanız gerekiyorsa, ihtiyacınız olan şey şudur:

sed -e 's/[]\/$*.^[]/\\&/g'

Ve tarafından kullanılabilir:

KEYWORD="The Keyword You Need";
ESCAPED_KEYWORD=$(printf '%s\n' "$KEYWORD" | sed -e 's/[]\/$*.^[]/\\&/g');

# Now you can use it inside the original sed statement to replace text
sed "s/$ESCAPED_KEYWORD/$ESCAPED_REPLACE/g"

/Sınırlayıcı dışında bir karakter kullanıyorsanız, kullandığınız karakterin üstündeki ifadelerde eğik çizgiyi değiştirmeniz gerekir. Açıklama için PeterJCLaw'ın yorumuna bakın.

Düzenlendi: Daha önce açıklanmayan bazı köşe vakaları nedeniyle, yukarıdaki komutlar birkaç kez değişti. Ayrıntılar için düzenleme geçmişini kontrol edin.


17
Öne eğik çizgileri ayırıcı olarak kullanmadan kaçmaktan kaçınabileceğinizi belirtmek gerekir. Sed'in çoğu (tümü?) Sürümü, desene uyduğu sürece herhangi bir karakteri kullanmanıza izin verir: $ echo 'foo / bar' | sed s _ / _: _ # foo: bar
PeterJCLaw

2
sed -e 's / (\ / \ | \\\ | &) / \\ & / g' OSX'te benim için çalışmadı, ancak bu şunu yapıyor: sed 's / ([\\\ / &]) / \\ & / g 've biraz daha kısa.
jcoffland

1
Arama modeli için KEYWORD, GNU^$s/[]\/$*.^|[]/\\&/g
sed'de

1
@Jesse: Sabit. Aslında, ilk paragrafta uyardığım hata budur. Sanırım vaaz ettiğim şeyi pratik yapmıyorum.
Pianosaurus

1
@NeronLeVelu: Emin ne demek istediğini, ancak "boru veya değişkenler içinde özel bir anlamı vardır biliyorum değilim Bu sonucun çalıştırmadan önce kabuk tarafından ayrıştırılır, değişkenler kadar içi çift tırnak güvenli Örneğin, çalıştırmayı deneyin.. A='foo"bar' echo $A | sed s/$A/baz/İçinde Çift tırnak işaretleri etrafındaki 'foo' ve 'bar' gibi ele alınır
Pianosaurus

92

Sed komutu, /ayırıcı yerine diğer karakterleri kullanmanıza izin verir :

sed 's#"http://www\.fubar\.com"#URL_FUBAR#g'

Çift tırnak işaretleri sorun değil.


5
.Aksi halde özel bir anlamı olan kaçmak zorundasınız . Cevabınızı düzenledim.
ypid

Ben sadece yapmaya çalıştım: sed '/CLIENTSCRIPT="foo"/a CLIENTSCRIPT2="hello"' fileile sed '|CLIENTSCRIPT="foo"|a CLIENTSCRIPT2="hello"' fileve bu aynı şeyi yapmaz.
Dimitri Kopriwa

1
Bu sadece ikame için geçerli olduğu için şunu söylemelidir: ssed komutu (ikame edildiği gibi) ayırıcı olarak / yerine başka karakterler kullanmanıza izin verir. Ayrıca, bu, eğik çizgi karakterleriyle URL'de sed'in nasıl kullanılacağına bir cevap olacaktır. OP sorusuna, /, \ içerebilen bir kullanıcı tarafından girilen bir dizeden nasıl çıkılacağını değil, aynı zamanda # kullanmaya karar verirseniz # da yanıt vermez. Ve ayrıca, URI # da içerebilir
papo

2
hayatımı değiştirdi! Teşekkür ederim!
Franciscon Santos

48

Değiştirme deyiminde özel olarak ele alınan yalnızca üç değişmez karakter /, \( cümleyi kapatmak için), (karakterlerden kaçmak için, geri başvuru, & c.) Ve &(eşleşmeyi değiştirmeye dahil etmek) şeklindedir. Bu nedenle, yapmanız gereken tek şey bu üç karakterden kaçmaktır:

sed "s/KEYWORD/$(echo $REPLACE | sed -e 's/\\/\\\\/g; s/\//\\\//g; s/&/\\\&/g')/g"

Misal:

$ export REPLACE="'\"|\\/><&!"
$ echo fooKEYWORDbar | sed "s/KEYWORD/$(echo $REPLACE | sed -e 's/\\/\\\\/g; s/\//\\\//g; s/&/\\\&/g')/g"
foo'"|\/><&!bar

Ayrıca yeni bir satır bence. Yeni satırdan nasıl kaçarım?
Alexander Gladysh

2
Ters eğik çizgilerle ilgili olarak yankının varsayılan davranışının ne olduğuna dikkat edin. Bash'da, echo varsayılan olarak, burada amaca hizmet eden ters eğik çizgi kaçışlarının yorumlanmasını önermez. Dash (sh) 'de ise yankı ters eğik çizgi kaçışlarını yorumlar ve bildiğim kadarıyla bunu bastırmanın hiçbir yolu yoktur. Bu nedenle, tire (sh) 'de echo $ x yerine printf'% s \ n '$ x yapın.
Youssef Eldakar

Ayrıca, kullanıcı girişindeki ters eğik çizgileri değişmez olarak değerlendirmek için bir okuma yaparken her zaman -r seçeneğini kullanın.
Youssef Eldakar

Diğer kabuklarla platformlar arası uyumluluk için, sed özel karakterlerinin değiştirilmesi ile ilgili bu belgeye başvurmalısınız: grymoire.com/Unix/Sed.html#toc-uh-62
Dejay Clayton

2
@Drux Üç karakter, değiştirme yan tümcesindeki tek özel karakterdir . Desen cümlesinde çok daha fazlası özeldir.
lenz

33

Pianosaurus'un düzenli ifadelerine dayanarak, hem anahtar kelimeden hem de değiştirmeden kaçan bir bash işlevi yaptım.

function sedeasy {
  sed -i "s/$(echo $1 | sed -e 's/\([[\/.*]\|\]\)/\\&/g')/$(echo $2 | sed -e 's/[\/&]/\\&/g')/g" $3
}

İşte nasıl kullanacağınız:

sedeasy "include /etc/nginx/conf.d/*" "include /apps/*/conf/nginx.conf" /etc/nginx/nginx.conf

3
Teşekkürler! Kullanmaya çalışırken başkası sözdizimi hatası alırsa, tıpkı benim gibi, sadece sh değil, bash kullanarak çalıştırmayı unutmayın
Konstantin Pereiaslov

1
Sed yerine sicim yerine dizgiden kaçmanın bir işlevi var mı?
CMCDragonkai

Hey, sadece boruların böyle bir yankı ile başlatılmasına ilişkin genel bir uyarı: Bazı (en çok?) Yankı uygulamaları seçenekler (bkz. man echo) Alır ve argümanınız $1bir çizgi ile başladığında borunun beklenmedik şekilde davranmasına neden olur . Bunun yerine borunuzu ile başlatabilirsiniz printf '%s\n' "$1".
Pianosaurus

17

Cevap vermek için biraz geç ... ama bunu yapmanın çok daha basit bir yolu var. Sadece sınırlayıcıyı değiştirin (yani, alanları ayıran karakter). Yani, s/foo/bar/yazmak yerine s|bar|foo.

Ve bunu yapmanın kolay yolu:

sed 's|/\*!50017 DEFINER=`snafu`@`localhost`\*/||g'

Ortaya çıkan çıktı, bu kötü DEFINER yan tümcesinden yoksundur.


10
Hayır, &`` hangisi seçilirse seçilsin, sınırlayıcı da yine de kaçmalı.
mirabilos

3
Yedek dize "/" karakterleri vardı, bu benim sorunum çözüldü. Teşekkürler dostum!
Evgeny Goldin

benim için çalışıyor. Yaptığım şey, $değiştirilmek üzere dizede kaçmaya çalışmak $ve yedek dizede anlamını korumaktır . $XXXDeğişken değerini değiştirmek istiyorum demek $YYY, sed -i "s|\$XXX|$YYY|g" fileiyi çalışıyor.
hakunami

11

Yanlış soru sorduğunuz ortaya çıkıyor. Yanlış soruyu da sordum. Yanlış olmasının nedeni ilk cümlenin başlangıcıdır: " Bash senaryomda ...".

Aynı soruyu sordum ve aynı hatayı yaptım. Eğer bash kullanıyorsanız, string değiştirmeleri yapmak için sed kullanmanıza gerek yoktur (ve bash içine yerleştirilen replace özelliğini kullanmak çok daha temizdir).

Örneğin, şöyle bir şey yerine:

function escape-all-funny-characters() { UNKNOWN_CODE_THAT_ANSWERS_THE_QUESTION_YOU_ASKED; }
INPUT='some long string with KEYWORD that need replacing KEYWORD.'
A="$(escape-all-funny-characters 'KEYWORD')"
B="$(escape-all-funny-characters '<funny characters here>')"
OUTPUT="$(sed "s/$A/$B/g" <<<"$INPUT")"

bash özelliklerini yalnızca şunları kullanabilirsiniz:

INPUT='some long string with KEYWORD that need replacing KEYWORD.'
A='KEYWORD'
B='<funny characters here>'
OUTPUT="${INPUT//"$A"/"$B"}"

BTW, sözdizimi vurgulama burada yanlış. Dış tırnaklar eşleşir ve iç tırnaklar eşleşir. Başka bir deyişle, benziyor $Ave $Btırnaksız, ama değil. Yapının içindeki ${}tırnaklar onun dışındaki tırnaklarla eşleşmiyor.
destenson

Aslında bir ödevin sağ tarafından alıntı yapmanız gerekmez (böyle bir şey yapmak istemiyorsanız var='has space') - OUTPUT=${INPUT//"$A"/"$B"}güvenlidir.
Benjamin W.

Aslında bir ödevin sağ tarafını alıntılamak zorunda değilsiniz (gerçek dünyada çalışmasını istemiyorsanız ve sadece yur mad skilz'i göstermek için bir oyuncak senaryosu olarak değil). Her zaman kabuğun yorumlamasını istemediğim her değişken genişlemeyi alıntılamaya çalışırım, eğer özel bir nedenim yoksa. Bu şekilde, özellikle yeni veya beklenmedik girdiler sağlandığında işler daha az kırılma eğilimindedir.
destenson

1
El kitabına bakın : "Tüm değerler tilde genişletme, parametre ve değişken genişletme, komut değiştirme, aritmetik genişletme ve alıntı kaldırma işleminden geçer (aşağıda ayrıntılı olarak açıklanmıştır)." Yani, çift tırnak işaretleri ile aynı.
Benjamin W.

1
Bir dosyada sed kullanmanız gerekiyorsa ne olur?
Efren

1

Awk kullanın - daha temiz:

$ awk -v R='//addr:\\file' '{ sub("THIS", R, $0); print $0 }' <<< "http://file:\_THIS_/path/to/a/file\\is\\\a\\ nightmare"
http://file:\_//addr:\file_/path/to/a/file\\is\\\a\\ nightmare

2
Sorun awkşu ki sed -i,% 99 oranında son derece kullanışlı olan benzer bir şey yok .
Tino

Bu, doğru yönde atılmış bir adımdır, ancak awk, yerine koyma işleminizdeki bazı meta karakterleri hala yorumlar, bu nedenle kullanıcı girişi için hala güvenli değildir.
Jeremy Huiskamp

0

İşte bir süre önce kullandığım bir AWK örneği. Yeni AWKS yazdıran bir AWK. AWK ve SED benzer olması iyi bir şablon olabilir.

ls | awk '{ print "awk " "'"'"'"  " {print $1,$2,$3} " "'"'"'"  " " $1 ".old_ext > " $1 ".new_ext"  }' > for_the_birds

Aşırı görünüyor, ancak bir şekilde tırnak kombinasyonu 'değişmez' olarak basılmaya devam ediyor. Sonra doğru hatırlıyorum vaiables sadece böyle tırnaklarla çevrili: "$ 1". Deneyin, SED ile nasıl çalıştığını bana bildirin.


0

Sekme gibi özel karakterlerle kırılacak olan sedeasy işlevi üzerinde bir gelişme var.

function sedeasy_improved {
    sed -i "s/$(
        echo "$1" | sed -e 's/\([[\/.*]\|\]\)/\\&/g' 
            | sed -e 's:\t:\\t:g'
    )/$(
        echo "$2" | sed -e 's/[\/&]/\\&/g' 
            | sed -e 's:\t:\\t:g'
    )/g" "$3"
}

Peki, ne farklı? $1ve $2kabuk genişlemelerini önlemek ve sekmeleri veya çift boşlukları korumak için tırnak işaretleri içine alınır.

Bir sekmeyi dönüştüren ek borular | sed -e 's:\t:\\t:g'( :jeton gibi ) \t.


Ama borularda yankı kullanma ile ilgili aşırı cevap hakkındaki yorumuma bakın.
Pianosaurus

0

Bunlar bulduğum kaçış kodları:

* = \x2a
( = \x28
) = \x29

" = \x22
/ = \x2f
\ = \x5c

' = \x27
? = \x3f
% = \x25
^ = \x5e

-1

"ve" etrafındaki kabuk sınırlamasıyla meydana gelen tüm hazları unutma

yani (ksh cinsinden)

Var=">New version of \"content' here <"
printf "%s" "${Var}" | sed "s/[&\/\\\\*\\"']/\\&/g' | read -r EscVar

echo "Here is your \"text\" to change" | sed "s/text/${EscVar}/g"

tam olarak ihtiyaç duyduğum yön, google aracılığıyla bulunan sonuçlardan kaçmak için bu yüzden - sed "s / [& \\\ * \\" \ '\ "') (] / \\ & / g '
MolbOrg

-1

Sed komutunda Değişken değerini değiştirmek istiyorsanız Örnek'i kaldırın:

sed -i 's/dev-/dev-$ENV/g' test to sed -i s/dev-/dev-$ENV/g test

-2

Durum, sedkalıbın yerine geçmek üzere rastgele bir şifre oluşturuyorsanız , rastgele dizede hangi karakter kümesine dikkat etmeyi seçersiniz. Bir değeri base64 olarak kodlayarak oluşturulan bir parola seçerseniz, yalnızca base64'te hem mümkün olan hem de seddeğiştirme deseninde özel bir karakter olan bir karakter vardır . Bu karakter "/" dir ve oluşturduğunuz şifreden kolayca kaldırılır:

# password 32 characters log, minus any copies of the "/" character.
pass=`openssl rand -base64 32 | sed -e 's/\///g'`;

-4

Bunu yapmanın daha kolay bir yolu, dizeyi elden önce oluşturmak ve bir parametre olarak kullanmaktır. sed

rpstring="s/KEYWORD/$REPLACE/g"
sed -i $rpstring  test.txt

REPLACE kullanıcı tarafından sağlandığı için başarısız ve son derece tehlikeli: REPLACE=/verirsed: -e expression #1, char 12: unknown option to `s'
Tino
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.