Różnica między pojedynczymi i podwójnymi cudzysłowami w Bash


Odpowiedzi:


577

Pojedyncze cudzysłowy niczego nie interpolują, ale podwójne cudzysłowy będą. Na przykład: zmienne, backticks, niektóre \sekwencje specjalne itp.

Przykład:

$ echo "$(echo "upg")"
upg
$ echo '$(echo "upg")'
$(echo "upg")

Podręcznik Bash ma następującą treść:

3.1.2.2 Pojedyncze cudzysłowy

Umieszczanie znaków w pojedynczych cudzysłowach ( ') zachowuje dosłowną wartość każdego znaku w cudzysłowie. Pojedynczy cytat może nie wystąpić między pojedynczymi cudzysłowami, nawet jeśli poprzedzony jest odwrotnym ukośnikiem.

3.1.2.3 Podwójne cudzysłowy

Załączając znaków w cudzysłowach ( ") chroni dosłowne wartości wszystkich znaków w cudzysłowie, z wyjątkiem $, `, \i, gdy ekspansja historia jest włączona !. Znaki $i `zachowują swoje specjalne znaczenie w podwójnych cudzysłowach (patrz Rozszerzenia powłoki ). Odwrotny ukośnik zachowuje specjalne znaczenie tylko wtedy, gdy następuje jedno z następujących znaków: $, `, ",\lub nowa linia. W ramach podwójnych cudzysłowów odwrotne ukośniki, po których następuje jeden z tych znaków, są usuwane. Ukośniki odwrotne poprzedzające znaki bez specjalnego znaczenia pozostają niezmodyfikowane. Podwójny cudzysłów można cytować w podwójnych cudzysłowach, poprzedzając go odwrotnym ukośnikiem. Jeśli ta opcja jest włączona, rozszerzanie historii będzie wykonywane, chyba że !pojawienie się podwójnego cudzysłowu zostanie poprzedzone znakiem odwrotnego ukośnika. Ukośnik odwrotny poprzedzający !nie jest usuwany.

Parametry specjalne *i @mają szczególne znaczenie, gdy w cudzysłowach (patrz Shell parametrów Expansion ).


40
Dla każdego, kto nie wie, co oznacza „interpolacja”: en.wikipedia.org/wiki/String_interpolation
Kanion Kolob

Co powiesz na to, że gdy używasz git_prompttego gita, sugerują używanie go w ten sposób PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ ', git zachęta , zgodnie z tym nie powinno działać. Czy jest coś specjalnego w PS#zmiennych? lub dlaczego to działa, jeśli nie wykonuje interpolacji.
ekiim

@ekiim Dokładny tekst jest ustawiony (niezmieniony) na PS1. Spróbuj echo $PS1zobaczyć, co mam na myśli. Ale PS1jest oceniany przed wyświetleniem (zobacz PROMPTINGsekcję na stronie podręcznika bash). Aby to przetestować, spróbuj PS1='$X'. Nie otrzymasz monitu. Następnie uruchom X=fooi nagle twój monit brzmi „foo” ( PS1został oceniony po ustawieniu zamiast wyświetlenia , nadal nie pojawił się monit).
Adam Batkin

262

The Odpowiedź akceptowana jest wielki. Przygotowuję tabelę, która pomaga w szybkim zrozumieniu tematu. Wyjaśnienie dotyczy prostej zmiennej, aa także tablicy indeksowanej arr.

Jeśli ustawimy

a=apple      # a simple variable
arr=(apple)  # an indexed array with a single element

a następnie echowyrażenie w drugiej kolumnie, otrzymalibyśmy wynik / zachowanie pokazane w trzeciej kolumnie. Czwarta kolumna wyjaśnia zachowanie.

 # | Expression  | Result      | Comments
---+-------------+-------------+--------------------------------------------------------------------
 1 | "$a"        | apple       | variables are expanded inside ""
 2 | '$a'        | $a          | variables are not expanded inside ''
 3 | "'$a'"      | 'apple'     | '' has no special meaning inside ""
 4 | '"$a"'      | "$a"        | "" is treated literally inside ''
 5 | '\''        | **invalid** | can not escape a ' within ''; use "'" or $'\'' (ANSI-C quoting)
 6 | "red$arocks"| red         | $arocks does not expand $a; use ${a}rocks to preserve $a
 7 | "redapple$" | redapple$   | $ followed by no variable name evaluates to $
 8 | '\"'        | \"          | \ has no special meaning inside ''
 9 | "\'"        | \'          | \' is interpreted inside "" but has no significance for '
10 | "\""        | "           | \" is interpreted inside ""
11 | "*"         | *           | glob does not work inside "" or ''
12 | "\t\n"      | \t\n        | \t and \n have no special meaning inside "" or ''; use ANSI-C quoting
13 | "`echo hi`" | hi          | `` and $() are evaluated inside ""
14 | '`echo hi`' | `echo hi`   | `` and $() are not evaluated inside ''
15 | '${arr[0]}' | ${arr[0]}   | array access not possible inside ''
16 | "${arr[0]}" | apple       | array access works inside ""
17 | $'$a\''     | $a'         | single quotes can be escaped inside ANSI-C quoting
18 | "$'\t'"     | $'\t'       | ANSI-C quoting is not interpreted inside ""
19 | '!cmd'      | !cmd        | history expansion character '!' is ignored inside ''
20 | "!cmd"      | cmd args    | expands to the most recent command matching "cmd"
21 | $'!cmd'     | !cmd        | history expansion character '!' is ignored inside ANSI-C quotes
---+-------------+-------------+--------------------------------------------------------------------

Zobacz też:


1
Przyjęta odpowiedź brzmi w końcu, The special parameters * and @ have special meaning when in double quoteswięc skąd "*"wynikają *?
anddero

2
@ Karl-AnderoMere, ponieważ w ogóle nie są one rozwijane jako parametry. "$@"i "$*"są rozszerzeniami parametrów. "@"i "*"nie są.
Charles Duffy,

@CharlesDuffy Dzięki, to ma teraz sens!
anddero

3
Numer 9 echo "\'", zwraca mi \'.
MaxGyver

@MaxGyver: dzięki za wskazanie. Zaktualizowałem odpowiedź.
codeforester

233

Jeśli odwołujesz się do tego, co się dzieje, gdy coś echo, pojedyncze cudzysłowy dosłownie odzwierciedlają to, co masz między nimi, podczas gdy podwójne cudzysłowy będą oceniać zmienne między nimi i generować wartość zmiennej.

Na przykład to

#!/bin/sh
MYVAR=sometext
echo "double quotes gives you $MYVAR"
echo 'single quotes gives you $MYVAR'

da to:

double quotes gives you sometext
single quotes gives you $MYVAR

11

Inni wyjaśnili bardzo dobrze i po prostu chcą podać proste przykłady.

Wokół tekstu można używać pojedynczych cudzysłowów, aby powłoka nie interpretowała żadnych znaków specjalnych. Znaki dolara, spacje, znaki handlowe, gwiazdki i inne znaki specjalne są ignorowane, jeśli są ujęte w pojedyncze cudzysłowy.

$ echo 'All sorts of things are ignored in single quotes, like $ & * ; |.' 

Da to:

All sorts of things are ignored in single quotes, like $ & * ; |.

Jedyną rzeczą, której nie można umieścić w pojedynczych cytatach, jest pojedynczy cytat.

Podwójne cudzysłowy działają podobnie do pojedynczych cudzysłowów, z tym że podwójne cudzysłowy nadal pozwalają powłoce interpretować znaki dolara, cudzysłowy i odwrotne ukośniki. Wiadomo już, że ukośniki odwrotne uniemożliwiają interpretację jednego znaku specjalnego. Może to być przydatne w przypadku podwójnych cudzysłowów, jeśli znak dolara musi być użyty jako tekst zamiast zmiennej. Pozwala również na ucieczkę podwójnych cudzysłowów, więc nie są one interpretowane jako koniec łańcucha cytowanego.

$ echo "Here's how we can use single ' and double \" quotes within double quotes"

Da to:

Here's how we can use single ' and double " quotes within double quotes

Można również zauważyć, że apostrof, który inaczej byłby interpretowany jako początek cytowanego łańcucha, jest ignorowany w podwójnych cudzysłowach. Zmienne są jednak interpretowane i zastępowane ich wartościami w podwójnych cudzysłowach.

$ echo "The current Oracle SID is $ORACLE_SID"

Da to:

The current Oracle SID is test

Cudzysłowy są całkowicie odmienne od pojedynczych lub podwójnych. Zamiast zapobiegać interpretacji znaków specjalnych, cudzysłowy faktycznie wymuszają wykonanie zawartych w nich poleceń. Po wykonaniu załączonych poleceń ich dane wyjściowe są zastępowane w miejscu pierwotnych cudzysłowów. Zostanie to wyjaśnione na przykładzie.

$ today=`date '+%A, %B %d, %Y'`
$ echo $today 

Da to:

Monday, September 28, 2015 

3

Istnieje wyraźne rozróżnienie między użyciem ' 'i " ".

Kiedy ' 'jest używany wokół czegokolwiek, nie wykonuje się „transformacji ani tłumaczenia”. Jest drukowane tak, jak jest.

Z " ", cokolwiek to otacza, jest „tłumaczone lub przekształcone” do jej wartości.

Przez tłumaczenie / transformację rozumiem, co następuje: Cokolwiek w pojedynczych cudzysłowach nie zostanie „przetłumaczone” na ich wartości. Zostaną zrobione, ponieważ znajdują się w cudzysłowie. Przykład:, a=23wtedy echo '$a'produkuje $ana standardowej mocy wyjściowej. Natomiast echo "$a"będzie produkować 23na standardowej mocy


1
Co rozumiesz przez „tłumaczenie” lub „transformacja”?
Nico Haase,

Ta odpowiedź jest dość myląca i nie dodaje niczego do istniejących dobrych odpowiedzi.
codeforester

2
To była, moim zdaniem, krótka zwięzła odpowiedź, bez nadmiernego wysiłku, co było dla mnie łatwe do zrozumienia. Mówiąc tłumaczenie / transformacja, mają na myśli podwójne cudzysłowy rozwiną zmienną, a pojedyncze cudzysłowy nie rozwiną zmiennej.
B_e_n_n_y_

1
Tak, to najlepsza odpowiedź. To powinna być zaakceptowana odpowiedź.
Jamey Kirby

3

Ponieważ jest to de facto odpowiedź na cytaty bash, dodam jeszcze jeden punkt pominięty w powyższych odpowiedziach, gdy mamy do czynienia z operatorami arytmetycznymi w powłoce.

bashShell obsługuje dwa sposoby zrobić operacja arytmetyczna, zdefiniowany przez wbudowanylet poleceniu i $((..))operatora. Pierwszy ocenia wyrażenie arytmetyczne, podczas gdy drugi jest bardziej złożonym wyrażeniem.

Ważne jest, aby zrozumieć, że używane jest wyrażenie arytmetyczne let dzieleniem słów, nazw ścieżek, podobnie jak inne polecenia powłoki. Dlatego należy wykonać prawidłowe cytowanie i ucieczkę.

Zobacz ten przykład podczas używania let

let 'foo = 2 + 1'
echo $foo
3

Używanie pojedynczych cudzysłowów tutaj jest absolutnie w porządku, ponieważ nie ma potrzeby tutaj zmiennych rozszerzeń, rozważ przypadek

bar=1
let 'foo = $bar + 1'

zawiodłoby nieszczęśliwie, ponieważ $barpojedyncze cytaty nie rozszerzyłyby się i należy je podwójnie podać jako

let 'foo = '"$bar"' + 1'

To powinien być jeden z powodów, dla $((..))których zawsze należy rozważyć nad używaniem let. Ponieważ w nim zawartość nie jest podzielona na słowa. Poprzedni przykład użycia letmożna po prostu zapisać jako

(( bar=1, foo = bar + 1 ))

Zawsze pamiętaj, aby używać $((..))bez pojedynczych cytatów

Chociaż $((..))można go używać z podwójnymi cudzysłowami, nie ma to żadnego celu, ponieważ nie może zawierać treści, które wymagałyby podwójnego cudzysłowu. Tylko upewnij się, że nie jest to pojedynczy cytat.

printf '%d\n' '$((1+1))'
-bash: printf: $((1+1)): invalid number
printf '%d\n' $((1+1))
2
printf '%d\n' "$((1+1))"
2

Mogą występować w niektórych szczególnych przypadkach użycia $((..))operatora w ciągu pojedynczego cudzysłowu, musisz interpolować cudzysłowy w taki sposób, aby operator nie pozostawił cudzysłowu lub był pod cudzysłowem. Np. Weź pod uwagę przypadek, gdy próbujesz użyć operatora wewnątrz curlinstrukcji, aby przekazać licznik za każdym razem, gdy żądanie jest wysyłane, wykonaj

curl http://myurl.com --data-binary '{"requestCounter":'"$((reqcnt++))"'}'

Zwróć uwagę na użycie zagnieżdżonych podwójnych cudzysłowów wewnątrz, bez których dosłowny ciąg znaków $((reqcnt++))jest przekazywany do requestCounterpola.


2
Charles Duffy sprawia dobrą sprawę tutaj na podwójne przytoczyć $((...))również. Może to być „trochę” paranoiczne i IFS=0na przykład mało prawdopodobne , ale na pewno nie jest niemożliwe :)
PesaThe

Istnieje również starsza $[[...]]składnia, ale może miałeś rację, zapominając o tym.
tripleee
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.