Bash kullanırken hangi karakterlerin kaçması gerekir?


206

Bash'te kaçması gereken kapsamlı bir karakter listesi var mı? Sadece ile kontrol edilebilir sedmi?

Özellikle %kaçması gerekip gerekmediğini kontrol ediyordum . denedim

echo "h%h" | sed 's/%/i/g'

ve kaçmadan iyi çalıştı %. Kaçmak %gerekmediği anlamına mı geliyor ? Bu gerekliliği kontrol etmek için iyi bir yol muydu?

Ve daha genel: kaçmak için aynı karakterler mi shellvebash mi ?


4
Genel olarak, umursuyorsanız, Yanlış Yapıyorsunuz demektir. Verilerin işlenmesi, asla kod için kullanılan ayrıştırma ve değerlendirme sürecinden geçerek kaçan tartışmalara yol açmamalıdır. Bu, SQL için en iyi uygulamalara çok yakın bir paraleldir - Doğru Şey bağlama değişkenlerini kullanmaktır ve Yanlış Şey dize ikameleriyle enjekte edilen verileri "sterilize etmeye" çalışmaktır.
Charles Duffy


8
@CharlesDuffy Evet, ama bazen hazırlanan ifadeler motorunun arka uçta yaptığı şey sadece kaçmak. Kullanıcı tarafından gönderilen yorumların tarayıcıda gösterilmesinden önce SO "bunu yanlış yapıyor" mu? Hayır. XSS'yi engelliyorlar. Hiç umursamamak yanlış yapıyor.
Part Atışı

@ParthianShot, hazırlanan ifade motoru verileri koddan tamamen bant dışı tutmuyorsa, yazan kişiler vurulmalıdır. Evet, MySQL'in tel protokolünün bu şekilde uygulandığını biliyorum; ifadem duruyor.
Charles Duffy

@CharlesDuffy Ve benim açımdan - bazen seçenekleriniz, bir şeyleri saflaştırmak için bir araç zinciri kullanarak güvenli bir şekilde çalışmak ya da onu güzelleştirmek için sekiz kez zaman ve çaba harcamaktır.
Parthian Shot

Yanıtlar:


283

Sadece içinde shdeğil , aynı zamanda çalışan iki kolay ve güvenli kural vardırbash .

1. Tüm dizeyi tek tırnak içine alın

Bu, tek tırnak dışındaki tüm karakterlerde işe yarar. Tek tırnaktan kaçmak için, önündeki alıntıyı kapatın, tek alıntıyı ekleyin ve alıntıyı tekrar açın.

'I'\''m a s@fe $tring which ends in newline
'

sed komutu: sed -e "s/'/'\\\\''/g; 1s/^/'/; \$s/\$/'/"

2. Bir ters eğik çizgi ile her karakter kaçmak

Bu, yeni satır dışındaki tüm karakterler için geçerlidir. Yeni satır karakterleri için tek veya çift tırnak kullanın. Boş teller hala ele alınmalıdır - ile değiştirin""

\I\'\m\ \a\ \s\@\f\e\ \$\t\r\i\n\g\ \w\h\i\c\h\ \e\n\d\s\ \i\n\ \n\e\w\l\i\n\e"
"

sed komutu: sed -e 's/./\\&/g; 1{$s/^$/""/}; 1!s/^/"/; $!s/$/"/'.

2b. Daha okunabilir 2 versiyonu

Daha kolay [a-zA-Z0-9,._+:@%/-]okunabilir olmasını sağlamak için kaçınılmaz olarak bırakılabilen, kolay ve güvenli bir karakter kümesi vardır

I\'m\ a\ s@fe\ \$tring\ which\ ends\ in\ newline"
"

sed komutu: LC_ALL=C sed -e 's/[^a-zA-Z0-9,._+@%/-]/\\&/g; 1{$s/^$/""/}; 1!s/^/"/; $!s/$/"/'.


Bir sed programında, son girdi satırının bir satırsonu baytı ile bitip bitmediğini bilemezsiniz (boş olduğu durumlar hariç). Bu yüzden her iki sed komutunun da olmadığını varsayıyor. Manuel olarak tırnak içine alınmış bir yeni satır ekleyebilirsiniz.

Kabuk değişkenlerinin yalnızca POSIX anlamda metin için tanımlandığını unutmayın. İkili verilerin işlenmesi tanımlanmamıştır. Önemli uygulamalar için ikili, NUL baytları dışında çalışır (çünkü değişkenler C dizeleriyle uygulanır ve C dizeleri, yani program bağımsız değişkenleri olarak kullanılması amaçlanır), ancak latin1 gibi bir "ikili" yerel ayara geçmelisiniz. .


(POSIX spesifikasyonunu okuyarak kuralları kolayca doğrulayabilirsiniz sh. Bash için @AustinPhillips tarafından bağlanan referans kılavuzuna bakın)


Not: # 1'de iyi bir varyasyon burada görülebilir: github.com/scop/bash-completion/blob/… . Çalışmayı gerektirmez sed, ancak gerektirir bash.
jwd

4
Not bu çalışma almak için mücadele herkes (benim gibi!) İçin .... OSX olsun sed lezzet gibi görünüyor bu sed komutları düzgün çalışmıyor. Yine de Linux üzerinde iyi çalışıyorlar!
dalelane

@dalelane: Burada test yapamıyorum. Lütfen her ikisinde de çalışan bir sürümünüz varsa düzenleyin.
Jo So

Dize bir '-' (eksi) ile başlamalı mı, yoksa yalnızca dosya adları için mi geçerli? - ikinci durumda önde bir './' gerekir.
slashmais

Ne anlatmak istediğinden emin değilim. Bu sed komutlarıyla giriş dizesi stdin'den alınır.
Jo So

59

kabuk girişi olarak yeniden kullanılabilen biçim

Bir Orada özel printf biçimi yönergesi ( %qistek bu tür inşa edilmiş):

printf [-v var] biçimi [bağımsız değişkenler]

 %q     causes printf to output the corresponding argument
        in a format that can be reused as shell input.

Bazı örnekler:

read foo
Hello world
printf "%q\n" "$foo"
Hello\ world

printf "%q\n" $'Hello world!\n'
$'Hello world!\n'

Bu, değişkenler aracılığıyla da kullanılabilir:

printf -v var "%q" "$foo
"
echo "$var"
$'Hello world\n'

Tüm (128) ascii baytları ile hızlı kontrol:

128 - 255 arasındaki tüm baytların kaçması gerektiğini unutmayın.

for i in {0..127} ;do
    printf -v var \\%o $i
    printf -v var $var
    printf -v res "%q" "$var"
    esc=E
    [ "$var" = "$res" ] && esc=-
    printf "%02X %s %-7s\n" $i $esc "$res"
done |
    column

Bunun gibi bir şey olması gerekir:

00 E ''         1A E $'\032'    34 - 4          4E - N          68 - h      
01 E $'\001'    1B E $'\E'      35 - 5          4F - O          69 - i      
02 E $'\002'    1C E $'\034'    36 - 6          50 - P          6A - j      
03 E $'\003'    1D E $'\035'    37 - 7          51 - Q          6B - k      
04 E $'\004'    1E E $'\036'    38 - 8          52 - R          6C - l      
05 E $'\005'    1F E $'\037'    39 - 9          53 - S          6D - m      
06 E $'\006'    20 E \          3A - :          54 - T          6E - n      
07 E $'\a'      21 E \!         3B E \;         55 - U          6F - o      
08 E $'\b'      22 E \"         3C E \<         56 - V          70 - p      
09 E $'\t'      23 E \#         3D - =          57 - W          71 - q      
0A E $'\n'      24 E \$         3E E \>         58 - X          72 - r      
0B E $'\v'      25 - %          3F E \?         59 - Y          73 - s      
0C E $'\f'      26 E \&         40 - @          5A - Z          74 - t      
0D E $'\r'      27 E \'         41 - A          5B E \[         75 - u      
0E E $'\016'    28 E \(         42 - B          5C E \\         76 - v      
0F E $'\017'    29 E \)         43 - C          5D E \]         77 - w      
10 E $'\020'    2A E \*         44 - D          5E E \^         78 - x      
11 E $'\021'    2B - +          45 - E          5F - _          79 - y      
12 E $'\022'    2C E \,         46 - F          60 E \`         7A - z      
13 E $'\023'    2D - -          47 - G          61 - a          7B E \{     
14 E $'\024'    2E - .          48 - H          62 - b          7C E \|     
15 E $'\025'    2F - /          49 - I          63 - c          7D E \}     
16 E $'\026'    30 - 0          4A - J          64 - d          7E E \~     
17 E $'\027'    31 - 1          4B - K          65 - e          7F E $'\177'
18 E $'\030'    32 - 2          4C - L          66 - f      
19 E $'\031'    33 - 3          4D - M          67 - g      

Birinci alanın onaltılık bayt değeri, ikincisi E karakterin kaçması gerekiyorsa ve üçüncü alan kaçan karakterin sunumunu gösterir.

Neden , ?

Her zaman kaçması gerekmeyen bazı karakterler görebilirsiniz ,, }ve gibi {.

Yani her zaman değil, bazen :

echo test 1, 2, 3 and 4,5.
test 1, 2, 3 and 4,5.

veya

echo test { 1, 2, 3 }
test { 1, 2, 3 }

ama dikkat:

echo test{1,2,3}
test1 test2 test3

echo test\ {1,2,3}
test 1 test 2 test 3

echo test\ {\ 1,\ 2,\ 3\ }
test  1 test  2 test  3

echo test\ {\ 1\,\ 2,\ 3\ }
test  1, 2 test  3 

Bu, pritnf'i bash / sh yoluyla çağırarak, dizenin önce bash / sh için kabuktan kaçması gerektiği
sorununa sahiptir

1
@ThorSummoner, dizeyi farklı bir dilden (muhtemelen nasıl alıntı yapacağınızı zaten bildiğiniz) kabuğa gerçek bir argüman olarak iletirseniz değil. Python: subprocess.Popen(['bash', '-c', 'printf "%q\0" "$@"', '_', arbitrary_string], stdin=subprocess.PIPE, stdout=subprocess.PIPE).communicate()size düzgün bir kabuk alıntı sürümünü verecektir arbitrary_string.
Charles Duffy

1
FYI bash %quzun bir süre kırıldı - Aklım bana iyi hizmet ederse, ~ 10 yıl kırıldıktan sonra 2013'te bir hata düzeltildi (ancak yine de kırık olabilir). Bu yüzden ona güvenme.
Jo So

@CharlesDuffy Tabii ki, Python arazisine girdikten sonra, shlex.quote()(> = 3.3, pipes.quote()- belgelenmemiş - eski sürümler için) da işi yapacak ve çoğu dizenin daha okunabilir bir sürümünü (gerektiği şekilde tırnak ekleyerek ve kaçarak) üretecek, bir kabuk doğmasına gerek kalmadan.
Thomas Perl

1
Hakkında özel notlar eklediğiniz için teşekkür ederiz ,. Yerleşik Bash'in printf -- %q ','verdiğini \,, ancak (kaçışını) /usr/bin/printf -- %q ','verdiğini öğrenmek beni şaşırttı ,. Diğer karakter için aynı: {, |, }, ~.
kevinarpe

34

İçinde ... KKO zorunda başkası kaydetmek için bash :

Çift tırnak karakterleri çevreleyen hariç, tırnak içindeki tüm karakterlerin değişmez değerini korur $, `, \, ve, geçmiş genişleme etkinken, !.

... öyleyse onlardan kaçarsanız (ve teklifin kendisi, elbette) muhtemelen iyisinizdir.

Daha kuşkulu bir 'şüphe duyduğunuzda kaçın' yaklaşımını kullanırsanız, tanımlayıcı karakterlerden (yani ASCII harfleri, rakamlar veya '_') kaçarak özel anlam taşıyan karakterleri almaktan kaçınmak mümkün olmalıdır. Bunların (yani bazı garip POSIX-ish kabuğunda) özel bir anlamı olması çok muhtemel değildir ve bu nedenle kaçmak gerekir.



Bu kısa, tatlı ve çoğunlukla doğru bir cevaptır (bunun için +1) ama belki de tek tırnak kullanmak daha iyidir - daha uzun cevabımı görün.
Jo So

26

Kullanılması print '%q' tekniği , biz özel olan karakterlerin öğrenmek için bir döngü çalıştırabilirsiniz:

#!/bin/bash
special=$'`!@#$%^&*()-_+={}|[]\\;\':",.<>?/ '
for ((i=0; i < ${#special}; i++)); do
    char="${special:i:1}"
    printf -v q_char '%q' "$char"
    if [[ "$char" != "$q_char" ]]; then
        printf 'Yes - character %s needs to be escaped\n' "$char"
    else
        printf 'No - character %s does not need to be escaped\n' "$char"
    fi
done | sort

Bu çıktıyı verir:

No, character % does not need to be escaped
No, character + does not need to be escaped
No, character - does not need to be escaped
No, character . does not need to be escaped
No, character / does not need to be escaped
No, character : does not need to be escaped
No, character = does not need to be escaped
No, character @ does not need to be escaped
No, character _ does not need to be escaped
Yes, character   needs to be escaped
Yes, character ! needs to be escaped
Yes, character " needs to be escaped
Yes, character # needs to be escaped
Yes, character $ needs to be escaped
Yes, character & needs to be escaped
Yes, character ' needs to be escaped
Yes, character ( needs to be escaped
Yes, character ) needs to be escaped
Yes, character * needs to be escaped
Yes, character , needs to be escaped
Yes, character ; needs to be escaped
Yes, character < needs to be escaped
Yes, character > needs to be escaped
Yes, character ? needs to be escaped
Yes, character [ needs to be escaped
Yes, character \ needs to be escaped
Yes, character ] needs to be escaped
Yes, character ^ needs to be escaped
Yes, character ` needs to be escaped
Yes, character { needs to be escaped
Yes, character | needs to be escaped
Yes, character } needs to be escaped

Bazı sonuçlar, ,biraz şüpheli görünüyor. @ CharlesDuffy bu konuda girdi almak ilginç olurdu.


2
Sen cevabını okuyabilirsiniz ,biraz şüpheli bakmak son fıkrasında Cevabıma
F. Hauri

2
%qKabuğu içinde karakteri kullanmayı planladığınızı bilmediğini unutmayın , böylece olası kabuk bağlamında özel bir anlamı olabilecek tüm karakterlerden kaçacaktır. ,Kendisinin kabuk için özel bir anlamı yoktur, ancak @ F.Hauri'nin cevabında işaret ettiği gibi, {...}brace genişlemesi içinde özel bir anlamı vardır : gnu.org/savannah-checkouts/gnu/bash/manual/… Bu gibi! ayrıca sadece belirli durumlarda genişleme gerektirir, genel olarak değil: echo Hello World!gayet iyi çalışır, ancak echo test!testbaşarısız olur.
Mecki

18

Kaçması gereken karakterler Bourne veya POSIX kabuğunda Bash'ten farklıdır. Genel olarak (çok) Bash bu kabukların bir süper seti, bu yüzden kaçtığınız her şeyshell kaçtığınız Bash'te kaçmalıdır.

Güzel bir genel kural "şüpheniz varsa kaçın" olacaktır. Ancak bazı karakterlerden kaçmak onlara özel bir anlam kazandırıyor \n. Bunlar ve man bashaltındaki sayfalarda listelenmiştir .Quotingecho

Bunun dışında alfasayısal olmayan herhangi bir karakterden kaçın, daha güvenlidir. Tek bir kesin liste bilmiyorum.

Man sayfaları hepsini bir yerde listeliyor, ama tek bir yerde değil. Dili öğrenin, emin olmanın yolu budur.

Beni yakalayan biri !. Bu, Bash'ta (ve csh) özel bir karakterdir (tarih genişletmesi), ancak Korn kabuğunda değil. Sorun bile echo "Hello world!"veriyor. Her zamanki gibi tek tırnak işareti kullanmak özel anlamı kaldırır.


1
Özellikle hoş bir genel kural "şüpheniz varsa, kaçmak" tavsiye olurdu gibi . Hala kontrol sededilmekten kaçınılması gerekip gerekmediğini görmek için yeterince iyi olup olmadığından şüphe duyun. Cevabınız için teşekkürler!
fedorqui 'SO' zarar vermeyi durdur '

2
@fedorqui: Kontrol etmek sedgerekli değildir, neredeyse her şeyi kontrol edebilirsiniz. sedsorun değil bash. Tek tırnak içinde özel karakterler (tek tırnak hariç) yoktur, oradaki karakterlerden bile kaçamazsınız. Bir sedkomut genellikle tek tırnak içinde olmalıdır, çünkü RE metakarakterlerinin güvenli olması için kabuk metakarakterleri ile çok fazla çakışma vardır. Bunun istisnası, dikkatle yapılması gereken kabuk değişkenlerinin gömülmesidir.
cdarke

5
İle kontrol edin echo. Ne koyduğunuzdan çıkarsanız kaçması gerekmez. :)
Mark Reed

6

Sanırım bash telleri hakkında konuşuyorsun. Kaçmak için farklı bir gereklilik setine sahip farklı dizeler vardır. Örneğin. Tek tırnak dizeleri çift tırnak dizelerinden farklıdır.

En iyi referans, bash kılavuzunun Alıntı bölümüdür.

Hangi karakterlerin kaçması gerektiğini açıklar. Geçmiş genişletme gibi hangi seçeneklerin etkinleştirildiğine bağlı olarak bazı karakterlerin kaçması gerekebileceğini unutmayın.


3
Bu yüzden kaçmanın kolay bir çözümü olmayan böyle bir orman olduğunu doğrular, her vakayı kontrol etmek zorunda kalacaktır. Teşekkürler!
fedorqui 'SO' zarar vermeyi durdur '

@fedorqui Her dilde olduğu gibi, uyulması gereken bir dizi kural vardır. Bash dizesinden kaçış için, kurallar kümesi kılavuzda açıklandığı gibi oldukça küçüktür. Hiçbir şey kaçmaya ihtiyaç duymadığından, kullanımı en kolay dize tek tırnaktır. Bununla birlikte, tek tırnak içine alınmış bir dizeye tek bir alıntı eklemenin bir yolu yoktur.
Austin Phillips

@fedorqui. Bu var olmayan bir orman. Kaçmak oldukça yapılabilir. Yeni yazıma bakın.
Jo So

@fedorqui Tek tırnaklı bir dize içinde tek bir alıntı kullanamazsınız, ancak "metin" "" "daha fazla metin"
CR.

4

Otomatik tamamlama kullanılırken bash'ın bazı karakterlerden otomatik olarak kaçtığını fark ettim.

Örneğin, adında bir dizininiz varsa dir:A, bash otomatik olarakdir\:A

Bunu kullanarak, ASCII tablosunun karakterlerini kullanarak bazı deneyler yürüttüm ve aşağıdaki listeleri türetilmiş:

Otomatik tamamlamada kaçan karakterler : (boşluk içerir)

 !"$&'()*,:;<=>?@[\]^`{|}

Bash karakterleri kaçmaz :

#%+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~

( /Dizin adlarında kullanılamayacağı için hariç tuttum )


2
Gerçekten kapsamlı bir listeye sahip olmak istiyorsanız, printf %qargüman olarak aktarıldığında hangi karakterlerin değiştiğine ve değişmediğine bakmanızı öneririm - ideal olarak, tüm karakter kümesinden geçerek.
Charles Duffy

Kesme işareti dizesiyle bile, özel karakterler üretmek için harflerden ve rakamlardan kaçmak isteyebilirsiniz. Örneğin: yeni satır karakterlerini sekme karakterlerine çeviren tr '\ n' '\ t'.
Dick Guertin
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.