Tek tırnaklı dizeler içinde tek tırnak nasıl kaçılır


1016

Diyelim ki Bash'iniz aliasvar:

alias rxvt='urxvt'

hangi iyi çalışıyor.

Ancak:

alias rxvt='urxvt -fg '#111111' -bg '#111111''

çalışmaz ve ikisi de çalışmaz:

alias rxvt='urxvt -fg \'#111111\' -bg \'#111111\''

Öyleyse, tırnaklardan kaçtıktan sonra bir dizenin içindeki tırnakların açılış ve kapanışını nasıl eşleştirebilirsiniz?

alias rxvt='urxvt -fg'\''#111111'\'' -bg '\''#111111'\''

onları bu şekilde birleştirmenize izin verilirse aynı dizeyi temsil etmesine rağmen tam anlamıyla görünüyor.


16
Takma ad için tek tırnak kullanmanız gerekmediğinin farkında mısınız? Çift tırnak işaretleri çok daha kolaydır.
teknopaul


3
Yuvalanmış çift tırnak işaretleri kaçınılmazdır, "\""bu yüzden mümkün olduğunda @ liori'nin cevabı yerine kullanılmalıdır.
alan

7
Çift tırnak işaretleri * nix'teki tek tırnaklardan (Bash ve Perl gibi ilgili araçlar dahil) oldukça farklı davranır, bu nedenle tek tırnaklarla ilgili bir sorun olduğunda çift tırnakların değiştirilmesi iyi bir çözüm DEĞİLDİR. Çift tırnaklar $ ... değişkenlerinin yerine getirilmeden önce yerine konulurken tek tırnaklar $ ... değişkenlerinin yerine tam anlamıyla davranılır.
Chuck Kollars

Düşünüyorsanız, çift ​​tırnak kullandım ama hala çalışmıyor , senaryonuzu tekrar kaynaklayın.
Samy Bencherif

Yanıtlar:


1454

En dıştaki katmanda gerçekten tek tırnak kullanmak istiyorsanız, her iki teklifi de yapıştırabileceğinizi unutmayın. Misal:

 alias rxvt='urxvt -fg '"'"'#111111'"'"' -bg '"'"'#111111'"'"
 #                     ^^^^^       ^^^^^     ^^^^^       ^^^^
 #                     12345       12345     12345       1234

Nasıl '"'"'yorumlandığının açıklaması ':

  1. ' Tek tırnak kullanılan ilk teklifi sonlandırın.
  2. " Çift tırnak kullanarak ikinci teklifi başlatın.
  3. ' Alıntılanan karakter.
  4. " Çift tırnak kullanarak ikinci teklifi bitirin.
  5. ' Tek tırnak kullanarak üçüncü teklifi başlatın.

(1) ve (2) arasına veya (4) ve (5) arasına boşluk koymazsanız, kabuk bu dizeyi tek bir uzun sözcük olarak yorumlayacaktır.


5
alias splitpath='echo $PATH | awk -F : '"'"'{print "PATH is set to"} {for (i=1;i<=NF;i++) {print "["i"]",$i}}'"'"Takma ad dizesinde hem tek tırnak hem de çift tırnak olduğunda çalışır!
Uphill_ '1

17
Benim yorumum: bash, farklı alıntılanmış dize ifadelerini örtük olarak birleştirir.
Benjamin Atkin

2
benim için çalıştı, çift kaçtı tek tırnak örneği:alias serve_this_dir='ruby -rrack -e "include Rack;Handler::Thin.run Builder.new{run Directory.new'"'"''"'"'}"'
JAMESSTONEco

2
Kesinlikle en okunabilir çözüm değil. Gerçekten ihtiyaç duyulmadıkları tek tırnakları aşırı kullanır.
oberlies

26
Bunun '\''çoğu bağlamda çok daha okunabilir olduğunu iddia ediyorum '"'"'. Aslında, birincisi hemen hemen her zaman tek tırnaklı bir dize içinde açıkça farklıdır ve bu nedenle onu \"çift ​​tırnaklı dizelerde olduğu gibi anlamsal olarak "bu bir kaçan alıntı" anlamıyla eşleme meselesidir . İkincisi, tek bir teklif kenesi çizgisine karışır ve birçok durumda düzgün bir şekilde ayırt etmek için dikkatli bir incelemeye ihtiyaç duyar.
mtraceur

263

Ben her zaman sadece her gömülü tek tırnak dizisi ile değiştirir: '\''(yani: alıntı ters eğik alıntı alıntı) dize kapatır, bir kaçan tek tırnak ekler ve dize yeniden açar.


Bunu benim için yapmak için sık sık Perl betiklerimdeki "quotify" işlevini kırıyorum. Adımlar şöyle olacaktır:

s/'/'\\''/g    # Handle each embedded quote
$_ = qq['$_']; # Surround result with single quotes.

Bu hemen hemen tüm vakalarla ilgilenir.

evalKabuk senaryolarınızı tanıttığınızda hayat daha eğlenceli hale gelir . Esasen her şeyi yeniden alıntılamak zorundasınız!

Örneğin, yukarıdaki ifadeleri içeren quotify adında bir Perl betiği oluşturun:

#!/usr/bin/perl -pl
s/'/'\\''/g;
$_ = qq['$_'];

daha sonra doğru şekilde tırnak içine alınmış bir dize oluşturmak için kullanın:

$ quotify
urxvt -fg '#111111' -bg '#111111'

sonuç:

'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''

daha sonra takma ad komutuna kopyalanabilir / yapıştırılabilir:

alias rxvt='urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''

(Komutu bir eval'e eklemeniz gerekiyorsa, alıntıyı tekrar çalıştırın:

 $ quotify
 alias rxvt='urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''

sonuç:

'alias rxvt='\''urxvt -fg '\''\'\'''\''#111111'\''\'\'''\'' -bg '\''\'\'''\''#111111'\''\'\'''\'''\'''

bir eval kopyalanabilir / yapıştırılabilir:

eval 'alias rxvt='\''urxvt -fg '\''\'\'''\''#111111'\''\'\'''\'' -bg '\''\'\'''\''#111111'\''\'\'''\'''\'''

1
Ama bu perl değil. Ve Steve B'nin yukarıda işaret ettiği gibi, "gnu referans kılavuzu" na yaptığı atıfla, aynı tür alıntı içinde bash'taki tırnaklardan kaçamazsınız. Ve aslında, alternatif tırnak içinde onları kaçmak gerekmez, örneğin, "'" geçerli tek tırnak dizedir ve '"' herhangi kaçan gerektirmeden geçerli bir çift tırnak dizedir.
nicerobot

8
@nicerobot: Aşağıdakileri gösteren bir örnek ekledim: 1) Aynı tür alıntı içinde tırnaklardan kaçmaya çalışmıyorum, 2) ne de alternatif alıntılarda ve 3) Perl geçerli bir oluşturma sürecini otomatikleştirmek için kullanılır tırnak işaretleri bash dize
Adrian Pronk

18
İlk paragraf tek başına aradığım cevaptı.
Dave Causey

9
Bash'ın yaptığı da budur, yazın set -xve echo "here's a string"bash'ın yürütüldüğünü göreceksiniz echo 'here'\''s a string'. ( set +xnormal davranışı döndürmek için)
arekolek

195

Bash 2.04 sözdizimi $'string'(sadece yerine 'string': uyarı: karıştırmayın $('string')) ANSI C benzeri kaçış dizilerine izin veren ve tek tırnaklı sürüme genişletme sağlayan başka bir tırnak mekanizmasıdır .

Basit örnek:

  $> echo $'aa\'bb'
  aa'bb

  $> alias myvar=$'aa\'bb'
  $> alias myvar
  alias myvar='aa'\''bb'

Senin durumunda:

$> alias rxvt=$'urxvt -fg \'#111111\' -bg \'#111111\''
$> alias rxvt
alias rxvt='urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''

Ortak kaçan diziler beklendiği gibi çalışır:

\'     single quote
\"     double quote
\\     backslash
\n     new line
\t     horizontal tab
\r     carriage return

Aşağıda man bash(sürüm 4.4) ' den kopyalanan + yapıştırılan ilgili belgeler bulunmaktadır :

$ 'String' biçimindeki kelimeler özel olarak ele alınır. Sözcük dizeye genişler, ters eğik çizgiden kaçan karakterler ANSI C standardında belirtildiği gibi değiştirilir. Ters eğik çizgi kaçış dizileri (varsa) şu şekilde çözülür:

    \a     alert (bell)
    \b     backspace
    \e
    \E     an escape character
    \f     form feed
    \n     new line
    \r     carriage return
    \t     horizontal tab
    \v     vertical tab
    \\     backslash
    \'     single quote
    \"     double quote
    \?     question mark
    \nnn   the eight-bit character whose value is the octal 
           value nnn (one to three digits)
    \xHH   the eight-bit character whose value is the hexadecimal
           value HH (one or two hex digits)
    \uHHHH the Unicode (ISO/IEC 10646) character whose value is 
           the hexadecimal value HHHH (one to four hex digits)
    \UHHHHHHHH the Unicode (ISO/IEC 10646) character whose value 
               is the hexadecimal value HHHHHHHH (one to eight 
               hex digits)
    \cx    a control-x character

Genişletilmiş sonuç, dolar işareti yokmuş gibi tek tırnak içine alınır.


Daha fazla bilgi için Tırnaklar ve kaçış: ANSI C gibi bash-hackers.org wiki dizeleri . Ayrıca "Bash Değişiklikleri" dosyasında ( genel bakış ) $'string'alıntılama mekanizmasıyla ilgili değişiklikler ve hata düzeltmelerinden çok bahsedildiğini unutmayın .

Unix.stackexchange.com'a göre Özel karakter normal karakter olarak nasıl kullanılır? bash, zsh, mksh, ksh93 ve FreeBSD ve busybox sh'de (bazı varyasyonlarla) çalışmalıdır.


kullanılabilir ancak buradaki tek tırnaklı dize gerçek bir tek tırnaklı değil, bu dize içeriği kabuk tarafından interprested edilebilir: echo $'foo\'b!ar'=> !ar': event not found
regilero 28:14

2
Benim makinede > echo $BASH_VERSION 4.2.47(1)-release > echo $'foo\'b!ar' foo'b!ar
mj41

1
Evet, "mayıs" ın nedeni budur, Kırmızı şapka 6.4, kesinlikle daha eski bir bash sürümü vardı.
regilero

Bash ChangeLog , $'muhtemelen en kolay yolla ilgili eski sistemlerde kendiniz denemekle ilgili birçok hata düzeltmesi içerir .
mj41

dikkat edin: e. Bash no longer inhibits C-style escape processing ($'...') while performing pattern substitution word expansions.Alındığı tiswww.case.edu/php/chet/bash/CHANGES . Hala 4.3.42'de çalışıyor, ancak 4.3.48'de çalışmıyor.
stiller_leser

49

Blogundaki girişi görmüyorum (link pls?) Ama gnu referans kılavuzuna göre :

Karakterleri tek tırnak içine almak ('' ') tırnak içindeki her karakterin gerçek değerini korur. Bir ters eğik çizgiden önce de olsa, tek tırnak işaretleri arasında tek bir tırnak bulunmayabilir.

yani bash anlamayacak:

alias x='y \'z '

ancak, çift tırnak işareti içine alırsanız bunu yapabilirsiniz:

alias x="echo \'y "
> x
> 'y


Çift tırnak içine alınmış içerikler değerlendirilmektedir, bu nedenle liori tarafından önerilen çift tırnak içine sadece tek tırnak eklemek uygun bir çözüm gibi görünmektedir.
Piotr Dobrogost

3
Bu sorunun asıl cevabı bu. Kabul edilen cevap bir çözüm sunabilse de, teknik olarak sorulmamış bir soruyu cevaplıyor.
Matthew G

3
Matthew, soru tek tırnak içine tek tırnak kaçmaktı. Bu yanıt kullanıcıdan davranışlarını değiştirmesini ister ve çift tırnak işareti kullanma konusunda bir engeliniz varsa (soru başlığının önerdiği gibi), bu cevap yardımcı olmaz. Yine de oldukça faydalıdır (açık olsa da) ve bu nedenle bir oylamayı hak ediyor, ancak kabul edilen cevap Op'un sorduğu kesin sorunu ele alıyor.
Fernando Cordeiro

Bir çift tırnak teli tek bir tırnak alıntı gerek yok.
Matthew D. Scholefield

32

'\''Tek tırnaklı bir dize içinde tek bir alıntı için kullanmanın Bash'de çalıştığını ve konunun önceki başlığındaki "yapıştırma" argümanı ile aynı şekilde açıklanabileceğini doğrulayabilirim . Alıntılanmış bir dizemiz olduğunu varsayalım: 'A '\''B'\'' C'(buradaki tüm alıntılar tek tırnaktır). O yankı geçirilir, bu aşağıdakileri yazdırır: A 'B' C. Her birinde '\''ilk tırnak geçerli tek tırnaklı dizgiyi kapatır, aşağıdaki \'tek bir alıntıyı önceki dizeye yapıştırır ( \'tırnak içine alınmış bir dizeye başlamadan tek bir tırnak belirtmenin bir yoludur) ve son tırnak bir tek tırnaklı dizeyi açar.


2
Bu yanıltıcıdır, bu '\' 'sözdizimi tek tırnak içine alınmış bir dize "içine" girmez. Bu ifadede 'A' \ '' B '\' 'C' 5 \ escape ve tek tırnak dizelerini birleştiriyorsunuz
teknopaul

1
@teknopaul atama alias something='A '\''B'\'' C'sonucu mu somethingolduğu için sağ taraftaki teknik olarak tek bir dize değil yani bile, tek bir dize olmak, ben çok hususlar gitmiyorumdur.
Teemu Leisti

Bu, örneğinizde çalışıyor olsa da, tek tırnaklı bir dizenin içine tek bir alıntı eklemek için teknik olarak bir çözüm sağlamaz . Zaten açıkladın, ama evet yapıyor . Başka bir deyişle, tek tırnaklı bir dizenin içine tek tırnak işareti karakterleri eklemek için bir çözüm, böyle bir dizeyi kendim oluşturmamı ve yazdırmamı sağlayacaktır. Ancak bu çözüm bu durumda çalışmaz. . Tasarlandığı gibi, BASH buna gerçekten izin vermez. 'A ' + ' + 'B' + ' + ' C'STR='\''; echo $STR
krb686

@mikhail_b, evet, '\''bash için çalışıyor. Gnu.org/software/bash/manual/bashref.html dosyasının hangi bölümlerinin böyle bir davranış belirttiğini söyleyebilir misiniz ?
Jingguo Yao

20

Her iki sürüm de, kaçan tek tırnak işareti (\ ') kullanılarak birleştirme veya tek tırnak karakteri çift tırnak ("" ") içine alınarak birleştirme ile çalışır.

Sorunun yazarı, son kaçma girişiminin sonunda ekstra bir tek alıntı (') olduğunu fark etmedi:

alias rxvt='urxvt -fg'\''#111111'\'' -bg '\''#111111'\''
           │         │┊┊|       │┊┊│     │┊┊│       │┊┊│
           └─STRING──┘┊┊└─STRIN─┘┊┊└─STR─┘┊┊└─STRIN─┘┊┊│
                      ┊┊         ┊┊       ┊┊         ┊┊│
                      ┊┊         ┊┊       ┊┊         ┊┊│
                      └┴─────────┴┴───┰───┴┴─────────┴┘│
                          All escaped single quotes    │
                                                       │
                                                       ?

Bir önceki güzel ASCII / Unicode sanatında görebileceğiniz gibi, son çıkan tek tırnak (\ ') yerine gereksiz bir tek tırnak (') gelir. Notepad ++ 'da olduğu gibi bir sözdizimi vurgulayıcı kullanmak çok yararlı olabilir.

Aynısı, aşağıdakine benzer başka bir örnek için de geçerlidir:

alias rc='sed '"'"':a;N;$!ba;s/\n/, /g'"'"
alias rc='sed '\'':a;N;$!ba;s/\n/, /g'\'

Bu iki güzel takma ad örneği, bir dosyanın nasıl dizilebileceğini çok karmaşık ve şaşkın bir şekilde gösterir. Yani, çok satır içeren bir dosyadan, önceki satırların içeriği arasında virgül ve boşluk içeren tek bir satır alırsınız. Önceki yorumun anlamını ifade etmek için aşağıdakiler bir örnektir:

$ cat Little_Commas.TXT
201737194
201802699
201835214

$ rc Little_Commas.TXT
201737194, 201802699, 201835214

3
ASCII Tablo çizimi için upwoted :)
php-dev

16

Kabuktan alıntılardan basit bir örnek:

$ echo 'abc'\''abc'
abc'abc
$ echo "abc"\""abc"
abc"abc

Zaten açılmış olanı ( ') bitirip kaçan birini ( \') yerleştirip başka bir tane ( ') açarak yapılır . Bu sözdizimi tüm komutlarda çalışır. İlk cevaba çok benzer bir yaklaşım.


15

Alıntı konusunu özellikle ele almıyorum çünkü, bazen, alternatif bir yaklaşım düşünmek mantıklı.

rxvt() { urxvt -fg "#${1:-000000}" -bg "#${2:-FFFFFF}"; }

daha sonra şu şekilde arayabilirsiniz:

rxvt 123456 654321

fikir şimdi tırnak için endişe olmadan bu takma olabilir:

alias rxvt='rxvt 123456 654321'

veya, herhangi bir #nedenle tüm çağrılara dahil etmeniz gerekirse :

rxvt() { urxvt -fg "${1:-#000000}" -bg "${2:-#FFFFFF}"; }

daha sonra şu şekilde arayabilirsiniz:

rxvt '#123456' '#654321'

o zaman, elbette, bir takma ad:

alias rxvt="rxvt '#123456' '#654321'"

(ayy, sanırım alıntıya değindim :)


1
Tek tırnak içine bir şey koymaya çalışıyordum, bu da tek tırnak içinde olan çift tırnaklardı. Amanın. "Farklı bir yaklaşım deneyin" yanıtınız için teşekkür ederiz. Farkı yarattı.
Clinton Blackmore

1
5 yıl geç kaldım, ancak son takma adınızda tek bir alıntı mı eksik?
Julien

1
@Julien Bir sorun görmüyorum ;-)
nicerobot

11

Tek tırnak içine alınmış dizelere tek tırnak koyamayacağından, en basit ve okunabilir seçenek HEREDOC dizesini kullanmaktır

command=$(cat <<'COMMAND'
urxvt -fg '#111111' -bg '#111111'
COMMAND
)

alias rxvt=$command

Yukarıdaki kodda, HEREDOC catkomuta gönderilir ve bunun çıktısı komut değiştirme notasyonu ile bir değişkene atanır$(..)

HEREDOC'un etrafına tek bir fiyat teklifi koymak, $()


Keşke daha önce aşağı kaydırsaydım - bu yaklaşımı yeniden keşfettim ve buraya göndermek için geldim! Bu, diğer tüm kaçan yaklaşımlardan çok daha temiz ve daha okunabilir. dashUbuntu uptart komut dosyalarında ve başka yerlerde varsayılan kabuk olan bazı bash olmayan kabuklarda çalışmaz .
Korny

Teşekkür ederim! aradığım şey, bir komutu heredoc aracılığıyla tanımlamanın ve otomatik kaçan komutu ssh'a geçirmenin yolu. BTW cat << tırnaksız KOMUT komuta içinde catiablelar enterpolasyon sağlar ve bu yaklaşım için de çalışır.
Igor Tverdovskiy

10

Sadece kabuk kodlarını kullanmak .. mesela \x27ya \\x22geçerliyse. Hiç güçlük yok, gerçekten.


Operasyonda bunun bir örneğini gösterebilir misiniz? Benim için sadece bir literal yazdırıyor x27(Centos 6.6'da)
Will Sheppard

6

Bu yanıtların çoğu, sorduğunuz özel davaya çarpıyor. Orada bir arkadaş olduğunu genel bir yaklaşımdır ve ben keyfi size ihtiyacı olduğunda alıntı için izin verdiğini geliştirdik için alıntı bash komutları ssh'dan kabuk genişleme, örneğin çoklu katmanları boyunca, su -c, bash -c, tek çekirdekli ilkel burada, lüzum yoktur vs. yerli bash:

quote_args() {
    local sq="'"
    local dq='"'
    local space=""
    local arg
    for arg; do
        echo -n "$space'${arg//$sq/$sq$dq$sq$dq$sq}'"
        space=" "
    done
}

Bu tam olarak söylediği şeyi yapar: her argümanı ayrı ayrı kabuklandırır (elbette bash genişletmesinden sonra):

$ quote_args foo bar
'foo' 'bar'
$ quote_args arg1 'arg2 arg2a' arg3
'arg1' 'arg2 arg2a' 'arg3'
$ quote_args dq'"'
'dq"'
$ quote_args dq'"' sq"'"
'dq"' 'sq'"'"''
$ quote_args "*"
'*'
$ quote_args /b*
'/bin' '/boot'

Bir genişleme katmanı için bariz bir şey yapar:

$ bash -c "$(quote_args echo a'"'b"'"c arg2)"
a"b'c arg2

( $(quote_args ...)Sonucun tek bir argüman haline getirilmesi için etraftaki çift tırnak işaretlerinin gerekli olduğunu unutmayın bash -c.) Ve daha genel olarak birden çok genişletme katmanı aracılığıyla düzgün bir şekilde alıntı yapmak için kullanılabilir:

$ bash -c "$(quote_args bash -c "$(quote_args echo a'"'b"'"c arg2)")"
a"b'c arg2

Yukarıdaki örnek:

  1. shell-her argümanı içsel olarak quote_argsayrı ayrı tırnak içine alır ve daha sonra elde edilen çıktıyı iç çift tırnak ile tek bir argümanda birleştirir.
  2. kabuk tırnak bash, -cve 1. adımdan itibaren zaten bir kere alıntılanan sonuç ve ardından dış çift tırnak ile tek bir argüman sonucu birleştirir.
  3. bu karışıklığı dışa argüman olarak gönderir bash -c.

Kısaca fikir budur. Bununla oldukça karmaşık şeyler yapabilirsiniz, ancak değerlendirme sırası ve hangi alt dizelerin alıntılandığı konusunda dikkatli olmalısınız. Örneğin, aşağıdakiler yanlış şeyler yapar (bazı "yanlış" tanımları için):

$ (cd /tmp; bash -c "$(quote_args cd /; pwd 1>&2)")
/tmp
$ (cd /tmp; bash -c "$(quote_args cd /; [ -e *sbin ] && echo success 1>&2 || echo failure 1>&2)")
failure

İlk örnekte, bash hemen quote_args cd /; pwd 1>&2iki ayrı komuta genişler quote_args cd /ve pwd 1>&2bu nedenle komut yürütüldüğünde CWD hala /tmpolur pwd. İkinci örnek, globbing için benzer bir sorunu göstermektedir. Aslında, aynı temel sorun tüm bash genişlemelerinde ortaya çıkar. Buradaki sorun, komut değiştirme işleminin bir işlev çağrısı olmamasıdır: kelimenin tam anlamıyla bir bash betiğini değerlendirir ve çıktısını başka bir bash betiğinin parçası olarak kullanır.

Kabuk işleçlerinden basitçe kaçmaya çalışırsanız, sonuçta elde edilen dize bash -c, yalnızca işleç olarak yorumlanmayan tek tek tırnaklı dizelerden oluşan bir dizidir, bu da dizeyi yankılandıracağınızı görmek kolaydır bash'a geçti:

$ (cd /tmp; echo "$(quote_args cd /\; pwd 1\>\&2)")
'cd' '/;' 'pwd' '1>&2'
$ (cd /tmp; echo "$(quote_args cd /\; \[ -e \*sbin \] \&\& echo success 1\>\&2 \|\| echo failure 1\>\&2)")
'cd' '/;' '[' '-e' '*sbin' ']' '&&' 'echo' 'success' '1>&2' '||' 'echo' 'failure' '1>&2'

Buradaki sorun fazla alıntı yapmanız. İhtiyacınız olan şey, operatörlerin çevreye giriş olarak ayrıştırılmamasıdır bash -c, yani $(quote_args ...)komut ikamesinin dışında olmaları gerekir .

Sonuç olarak, en genel anlamda yapmanız gereken, komut ikamesi sırasında ayrı olarak genişletilmesi amaçlanmayan komutun her bir kelimesini ayrı ayrı kabuk-alıntı yapmak ve kabuk operatörlerine herhangi bir ekstra alıntı uygulamak değildir:

$ (cd /tmp; echo "$(quote_args cd /); $(quote_args pwd) 1>&2")
'cd' '/'; 'pwd' 1>&2
$ (cd /tmp; bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")
/
$ (cd /tmp; echo "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")
'cd' '/'; [ -e *'sbin' ] && 'echo' 'success' 1>&2 || 'echo' 'failure' 1>&2
$ (cd /tmp; bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")
success

Bunu yaptıktan sonra, dizenin tamamı keyfi değerlendirme seviyelerine daha fazla alıntı yapmak için adil bir oyundur:

$ bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")"
/
$ bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")")"
/
$ bash -c "$(quote_args bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")")")"
/
$ bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")"
success
$ bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *sbin ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")")"
success
$ bash -c "$(quote_args bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")")")"
success

vb.

Bu örnekler sinirleri sözler gibi göz önüne alındığında görünebilir success, sbinve pwdkabuk-alıntılanan olması gerekmez, ama keyfi girdi alarak bir senaryo yazarken önemli nokta hatırlamak size kesinlikle emin değiliz herşeyi alıntı yapmak istiyorum olmasıdır 'doesn t Alıntı yapmak gerekmez, çünkü bir kullanıcının ne zaman atacağını asla bilemezsiniz Robert'; rm -rf /.

Kapakların altında neler olup bittiğini daha iyi anlamak için iki küçük yardımcı işlevle oynayabilirsiniz:

debug_args() {
    for (( I=1; $I <= $#; I++ )); do
        echo -n "$I:<${!I}> " 1>&2
    done
    echo 1>&2
}

debug_args_and_run() {
    debug_args "$@"
    "$@"
}

bir komutu yürütmeden önce her bir argümanı numaralandıracak:

$ debug_args_and_run echo a'"'b"'"c arg2
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2

$ bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)"
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2

$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'> 
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2

$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'echo'"'"' '"'"'a"b'"'"'"'"'"'"'"'"'c'"'"' '"'"'arg2'"'"''> 
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'> 
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2

$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")")")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'bash'"'"' '"'"'-c'"'"' '"'"''"'"'"'"'"'"'"'"'debug_args_and_run'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'echo'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'a"b'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'c'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'arg2'"'"'"'"'"'"'"'"''"'"''> 
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'echo'"'"' '"'"'a"b'"'"'"'"'"'"'"'"'c'"'"' '"'"'arg2'"'"''> 
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'> 
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2

Merhaba Kyle. Bir grup argümanı tek bir argüman olarak iletmem gerektiğinde, çözümünüz sahip olduğum bir dava için harika çalıştı vagrant ssh -c {single-arg} guest. {single-arg}Serseri konuk isminin ondan sonra gelecek argümansız çünkü ihtiyaçlar tek arg tedavi edilecek. Sipariş değiştirilemez. Ama bir komutu ve argümanlarını içinden geçirmem gerekiyordu {single-arg}. Yani Seninkini kullanılan quote_args()komut ve args alıntı ve sonuç sonuna çift tırnak koymak ve bir cazibe gibi çalıştı: vagrant ssh -c "'command' 'arg 1 with blanks' 'arg 2'" guest. Teşekkürler!!!
Andreas Maier

6

IMHO asıl cevap, tek tırnaklı dizeler içinde tek tırnaktan kaçamayacağınızdır.

Bu imkansız.

Eğer varsayarsak bash kullanıyoruz.

Bash el kitabından ...

Enclosing characters in single quotes preserves the literal value of each
character within the quotes.  A single quote may not occur
between single quotes, even when preceded by a backslash.

Diğer dize kaçış mekanizmalarından birini kullanmanız gerekir "veya \

aliasTek tırnak kullanmasını talep eden sihirli bir şey yok .

Her ikisi de bash'da çalışır.

alias rxvt="urxvt -fg '#111111' -bg '#111111'"
alias rxvt=urxvt\ -fg\ \'#111111\'\ -bg\ \'#111111\'

İkincisi, boşluk karakterinden kaçmak için \ kullanır.

Ayrıca # 111111 hakkında tek tırnak gerektiren sihirli bir şey yoktur.

Aşağıdaki seçenekler, diğer iki seçenekle aynı sonucu elde eder, çünkü rxvt diğer adı beklendiği gibi çalışır.

alias rxvt='urxvt -fg "#111111" -bg "#111111"'
alias rxvt="urxvt -fg \"#111111\" -bg \"#111111\""

Ayrıca zahmetli # doğrudan kaçabilirsiniz

alias rxvt="urxvt -fg \#111111 -bg \#111111"

"gerçek cevap, tek tırnaklı dizelerde tek tırnaktan kaçamayacağınızdır." Bu teknik olarak doğrudur. Ancak tek bir alıntı ile başlayan, tek bir alıntı ile biten ve sadece ortasında tek tırnak içeren bir çözümünüz olabilir. stackoverflow.com/a/49063038
wisbucky

Kaçarak değil, sadece birleştirme ile.
teknopaul

4

Verilen örnekte, dış kaçış mekanizması olarak tek tırnak yerine çift tırnak kullanılmıştır:

alias rxvt="urxvt -fg '#111111' -bg '#111111'"

Bu yaklaşım, bir komuta sabit bir dize geçirmek istediğiniz birçok durum için uygundur: Kabuğun çift tırnaklı dizeyi bir aracılığıyla nasıl yorumlayacağını kontrol edin echove gerekirse karakterlerden ters eğik çizgi ile kaçın.

Örnekte, dizeyi korumak için çift tırnak işaretlerinin yeterli olduğunu göreceksiniz:

$ echo "urxvt -fg '#111111' -bg '#111111'"
urxvt -fg '#111111' -bg '#111111'

4

Açıkçası, çift tırnak işaretleri ile çevrelenmek daha kolay olurdu, ama bunun içindeki zorluk nerede? İşte sadece tek tırnak kullanılan cevap. Bunun yerine bir değişken kullanıyorum, aliasbu yüzden kanıt için yazdırmak daha kolay, ama kullanmakla aynı alias.

$ rxvt='urxvt -fg '\''#111111'\'' -bg '\''#111111'\'
$ echo $rxvt
urxvt -fg '#111111' -bg '#111111'

açıklama

Anahtar, tek alıntıyı kapatıp istediğiniz kadar tekrar açabilmenizdir. Örneğin foo='a''b', aynıfoo='ab' . Böylece tek bir alıntıyı kapatabilir, gerçek bir tek tırnak içine atabilir ve bir \'sonraki tek alıntıyı tekrar açabilirsiniz .

Arıza diyagramı

Bu şema, tek tırnakların nerede açıldığını ve kapatıldığını göstermek için parantez kullanarak bunu netleştirir. Tırnaklar parantez gibi "iç içe" değildir. Ayrıca, doğru şekilde uygulanan renk vurgulamasına da dikkat edebilirsiniz. Alıntılanan teller bordo, oysa \'siyah.

'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'    # original
[^^^^^^^^^^] ^[^^^^^^^] ^[^^^^^] ^[^^^^^^^] ^    # show open/close quotes
 urxvt -fg   ' #111111  '  -bg   ' #111111  '    # literal characters remaining

(Bu aslında Adrian ile aynı cevap, ama bunun daha iyi açıkladığını hissediyorum. Ayrıca cevabının sonunda 2 gereksiz tek tırnak var.)


'\''Yöntemi kullanmak için +1, '"'"'insanların okuması genellikle daha zor olan yöntem üzerinden öneririm .
mtraceur

3

Yukarıda başvurulan The One True Cevabı ile ilgili bir detay:

Bazen ssh üzerinde rsync kullanarak indireceğim ve 'TWICE' ile bir dosya adından kaçmak zorundayım! (OMG!) Bir kez bash için ve bir kez ssh için. Değişken teklif sınırlayıcılarının aynı prensibi burada iş başında.

Örneğin, almak istediğimizi varsayalım: Louis Theroux'un LA Hikayeleri ...

  1. Önce Louis Theroux'u bash için tek tırnak, ssh için çift tırnak içine alırsınız: "" Louis Theroux "
  2. Sonra bir çift tırnak kaçmak için tek tırnak kullanın '' '
  3. Kesme işareti kaçmak için çift tırnak kullanın "" "
  4. Ardından, çift tırnaktan kaçmak için tek tırnak kullanarak # 2'yi tekrarlayın '' '
  5. Daha sonra LA Hikayelerini bash için tek tırnak, ssh için çift tırnak içine alın: "" LA Stories "

Ve bakın! Bununla kurcalarsınız:

rsync -ave ssh '"Louis Theroux"''"'"'"'"''"s LA Stories"'

ki bu küçük bir iş için çok fazla iştir '- ama işte gidiyorsunuz


3
shell_escape () {
    printf '%s' "'${1//\'/\'\\\'\'}'"
}

Uygulama açıklaması:

  • çift ​​tırnak içine alın, böylece tek tırnakları kolayca sarabilir ve ${...}sözdizimini kullanabiliriz

  • bash ara ve değiştir şöyle görünür: ${varname//search/replacement}

  • Biz değiştiriyoruz 'ile'\''

  • '\''şöyle tek bir kodlar ':

    1. ' tek alıntıyı bitirir

    2. \'kodlar bir '(biz tırnak içine değil, çünkü ters eğik çizgi tabi)

    3. ' tekrar tek bir alıntı yapmaya başlar

    4. bash, aralarında boşluk olmadan dizeleri otomatik olarak birleştirir

  • \her şeyden önce bir tane var \ve 'bu kaçış kuralları ${...//.../...}.

string="That's "'#@$*&^`(@#'
echo "original: $string"
echo "encoded:  $(shell_escape "$string")"
echo "expanded: $(bash -c "echo $(shell_escape "$string")")"

PS Her zaman tek tırnaklı dizeleri kodlayın, çünkü bunlar çift tırnaklı dizelerden çok daha basittir.


2

Çok fazla iç içe tırnak katmanı sorununu çözmenin başka bir yolu:

Çok küçük bir alana çok fazla sıkıştırmaya çalışıyorsunuz, bu yüzden bash işlevini kullanın.

Sorun şu ki, çok fazla yuvalama düzeyine sahip olmaya çalışıyorsunuz ve temel takma ad teknolojisi uyum sağlayacak kadar güçlü değil. Bunu yapmak için böyle bir bash işlevini kullanın, böylece tek, çift tırnak geri keneler ve geçirilen parametreler normalde beklediğimiz gibi işlenir:

lets_do_some_stuff() {
    tmp=$1                       #keep a passed in parameter.
    run_your_program $@          #use all your passed parameters.
    echo -e '\n-------------'    #use your single quotes.
    echo `date`                  #use your back ticks.
    echo -e "\n-------------"    #use your double quotes.
}
alias foobarbaz=lets_do_some_stuff

Daha sonra, 1 $ ve 2 $ değişkenlerinizi ve tek, çift tırnak ve arka keneleri, bütünlüklerini bozan takma işlev hakkında endişelenmeden kullanabilirsiniz.

Bu program yazdırır:

el@defiant ~/code $ foobarbaz alien Dyson ring detected @grid 10385
alien Dyson ring detected @grid 10385
-------------
Mon Oct 26 20:30:14 EDT 2015
-------------

2

GNU Parallel kuruluysa dahili alıntısını kullanabilirsiniz:

$ parallel --shellquote
L's 12" record
<Ctrl-D>
'L'"'"'s 12" record'
$ echo 'L'"'"'s 12" record'
L's 12" record

20190222 sürümünden --shellquotebirden çok kez bile yapabilirsiniz :

$ parallel --shellquote --shellquote --shellquote
L's 12" record
<Ctrl-D>
'"'"'"'"'"'"'L'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'s 12" record'"'"'"'"'"'"'
$ eval eval echo '"'"'"'"'"'"'L'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'s 12" record'"'"'"'"'"'"'
L's 12" record

Dizeyi tüm desteklenen kabuklarda (yalnızca değil bash) tırnak içine alacaktır .


1

Bu işlev:

quote () 
{ 
    local quoted=${1//\'/\'\\\'\'};
    printf "'%s'" "$quoted"
}

'içeriden alıntı yapılmasına izin verir '. Şu şekilde kullanın:

$ quote "urxvt -fg '#111111' -bg '#111111'"
'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''

Alıntı yapılacak satır, tek tırnaklarla karıştırılan çift tırnaklar gibi daha karmaşık hale gelirse, dizenin bir değişken içinde alıntı yapmasını sağlamak oldukça zor olabilir. Bu gibi durumlar ortaya çıktığında, bir komut dosyasının içine alıntı yapmanız gereken tam satırı yazın (buna benzer).

#!/bin/bash

quote ()
{
    local quoted=${1//\'/\'\\\'\'};
    printf "'%s'" "$quoted"
}

while read line; do
    quote "$line"
done <<-\_lines_to_quote_
urxvt -fg '#111111' -bg '#111111'
Louis Theroux's LA Stories
'single quote phrase' "double quote phrase"
_lines_to_quote_

Çıktı olacak:

'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''
'Louis Theroux'\''s LA Stories'
''\''single quote phrase'\'' "double quote phrase"'

Tek tırnak içinde tüm doğru tırnaklar.


1

Python 2 veya Python 3 içinde kabuk dizesi oluşturuyorsanız, aşağıdakiler bağımsız değişkenleri alıntılamaya yardımcı olabilir:

#!/usr/bin/env python

from __future__ import print_function

try:  # py3
    from shlex import quote as shlex_quote
except ImportError:  # py2
    from pipes import quote as shlex_quote

s = """foo ain't "bad" so there!"""

print(s)
print(" ".join([shlex_quote(t) for t in s.split()]))

Bu çıktı:

foo ain't "bad" so there!
foo 'ain'"'"'t' '"bad"' so 'there!'

1

İşte benim iki sent - eğer biri shsadece -belirli değil, taşınabilir olmak istiyorsa bash(çözüm çok verimli değildir, ancak harici bir program başlattığından - sed):

  • Bu in koymak quote.sh(veya yalnızca quotesizinle ilgili şu yerde) PATH:
# Bu standart girdi ile çalışır (stdin)
alıntı() {
  eko-n "'";
  sed 's / \ ([' "'"'] ['"' ''] * \) / '"' '' '\ 1 "' '' '' / g ';
  echo -n "'"
}

durumda "1 $"
 -) alıntı ;;
 *) echo "usage: cat ... | quote - Bourne kabuğu için # tek tırnak girişi" 2> & 1 ;;
esac

Bir örnek:

$ echo -n "Günaydın dostum!" | ./quote.sh -
'İyi günler dostum!'

Ve elbette, bu geri dönüş yapıyor:

$ echo 'G' "'"' day dostum! '
İyi günler dostum!

Açıklama: temelde tırnak girdi içine zorunda 've sonra da bu mikro canavar ile içinde tek tırnak değiştirin: '"'"'(a eşleştirme ile açılış teklifi sona 'çift tırnak ile sararak tek alıntı buldum kaçış - "'"ve Daha sonra nihayet yeni açılış bekar alıntı sorunu 'veya sözde gösterimde: ' + "'" + ' == '"'"')

Bunu yapmanın standart bir yolu sedaşağıdaki ikame komutuyla kullanmak olacaktır :

s/\(['][']*\)/'"\1"'/g 

Bununla birlikte, küçük bir problem, bunu kabukta kullanmak için, sed ifadesinin kendisindeki tüm bu tek tırnak karakterlerinden kaçmak gerektiğidir - ne gibi bir şeye yol açar

sed 's/\(['"'"']['"'"']*\)/'"'"'"\1"'"'"'/g' 

(ve bu sonucu oluşturmanın iyi bir yolu, orijinal ifadeyi s/\(['][']*\)/'"\1"'/gKyle Rose 'veya George V. Reilly'nin senaryolarına beslemektir ).

Son olarak, girdinin gelmesini beklemek mantıklıdır stdin- çünkü komut satırı argümanlarından geçmek zaten çok fazla sorun olabilir.

(Oh, ve belki küçük bir yardım mesajı eklemek istiyoruz, böylece birisi ./quote.sh --helpne yaptığını merak ettiği gibi çalıştırdığında komut dosyasının asılmaması gerekir .)


0

İşte başka bir çözüm. Bu işlev, tek bir argüman alacak ve yukarıdaki oylama cevabının açıkladığı gibi, tek tırnak karakterini kullanarak uygun şekilde alıntı yapacaktır:

single_quote() {
  local quoted="'"
  local i=0
  while [ $i -lt ${#1} ]; do
    local ch="${1:i:1}"
    if [[ "$ch" != "'" ]]; then
      quoted="$quoted$ch"
    else
      local single_quotes="'"
      local j=1
      while [ $j -lt ${#1} ] && [[ "${1:i+j:1}" == "'" ]]; do
        single_quotes="$single_quotes'"
        ((j++))
      done
      quoted="$quoted'\"$single_quotes\"'"
      ((i+=j-1))
    fi
    ((i++))
  done
  echo "$quoted'"
}

Yani, bu şekilde kullanabilirsiniz:

single_quote "1 2 '3'"
'1 2 '"'"'3'"'"''

x="this text is quoted: 'hello'"
eval "echo $(single_quote "$x")"
this text is quoted: 'hello'
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.