GNU/Linux >> Znalost Linux >  >> Linux

Kdy je nutné dvojité uvozování?

Stará rada bývala uvozovat jakýkoli výraz obsahující $VARIABLE , alespoň pokud bychom chtěli, aby to bylo interpretováno shellem jako jedna jediná položka, jinak jakékoli mezery v obsahu $VARIABLE by shodil skořápku.

Chápu však, že v novějších verzích shellů již není dvojité uvozování vždy potřeba (alespoň pro výše popsaný účel). Například v bash :

% FOO='bar baz'
% [ $FOO = 'bar baz' ] && echo OK
bash: [: too many arguments
% [[ $FOO = 'bar baz' ]] && echo OK
OK
% touch 'bar baz'
% ls $FOO
ls: cannot access bar: No such file or directory
ls: cannot access baz: No such file or directory

V zsh , na druhou stranu, stejné tři příkazy uspějí. Na základě tohoto experimentu se tedy zdá, že v bash , lze vynechat dvojité uvozovky uvnitř [[ ... ]] , ale ne uvnitř [ ... ] ani v argumentech příkazového řádku, zatímco v zsh , mohou být dvojité uvozovky ve všech těchto případech vynechány.

Ale odvodit obecná pravidla z neoficiálních příkladů, jako je ten výše, je náhodný návrh. Bylo by hezké vidět souhrn toho, kdy je nutné dvojité uvozování. Primárně mě zajímá zsh , bash a /bin/sh .

Přijatá odpověď:

Nejprve oddělte zsh od zbytku. Není to záležitost starých a moderních shellů:zsh se chová jinak. Návrháři zsh se rozhodli, že je nekompatibilní s tradičními shelly (Bourne, ksh, bash), ale snáze se používá.

Za druhé, je mnohem jednodušší používat dvojité uvozovky neustále, než si pamatovat, kdy jsou potřeba. Jsou potřeba většinu času, takže se budete muset učit, když nejsou potřeba, ne když jsou potřeba.

Stručně řečeno, dvojité uvozovky jsou nutné všude tam, kde se očekává seznam slov nebo vzor . Jsou volitelné v kontextech, kde analyzátor očekává nezpracovaný řetězec.

Co se stane bez uvozovek

Všimněte si, že bez dvojitých uvozovek se stanou dvě věci.

  1. Zaprvé výsledek expanze (hodnota proměnné pro substituci parametru jako ${foo} , nebo výstup příkazu pro náhradu příkazu jako $(foo) ) se rozdělí na slova všude tam, kde obsahuje mezery.
    Přesněji řečeno, výsledek rozbalení je rozdělen na každý znak, který se objeví v hodnotě IFS proměnná (znak oddělovače). Pokud sekvence oddělovacích znaků obsahuje mezery (mezera, tabulátor nebo nový řádek), mezery se počítají jako jeden znak; úvodní, koncové nebo opakované oddělovače bez mezer vedou k prázdným polím. Například pomocí IFS=" :" , :one::two : three: :four  vytváří prázdná pole před one , mezi one a two , a (jeden) mezi three a four .
  2. Každé pole, které je výsledkem rozdělení, je interpretováno jako globus (zástupný znak), pokud obsahuje jeden ze znaků [*? . Pokud se tento vzor shoduje s jedním nebo více názvy souborů, bude vzor nahrazen seznamem odpovídajících názvů souborů.

Nekótovaná expanze proměnné $foo je hovorově známý jako "operátor split+glob", na rozdíl od "$foo" která pouze přebírá hodnotu proměnné foo . Totéž platí pro substituci příkazů:"$(foo)" je náhrada příkazu, $(foo) je substituce příkazu následovaná split+glob.

Kde můžete vynechat dvojité uvozovky

Zde jsou všechny případy, které mě napadají v shellu ve stylu Bourne, kde můžete napsat substituci proměnné nebo příkazu bez uvozovek a hodnota je interpretována doslovně.

  • Na pravé straně úkolu.

    var=$stuff
    a_single_star=*
    

    Všimněte si, že za export potřebujete dvojité uvozovky , protože je to obyčejná vestavěná funkce, nikoli klíčové slovo. To platí pouze pro některé shelly, jako je pomlčka, zsh (v emulaci sh), yash nebo posh; bash i ksh zacházejí s export speciálně.

    export VAR="$stuff"
    
  • V case prohlášení.

    case $var in …
    

    Všimněte si, že potřebujete dvojité uvozovky ve vzoru velkých a malých písmen. K dělení slov nedochází ve vzoru velkých a malých písmen, ale proměnná bez uvozovek je interpretována jako vzor, ​​zatímco proměnná v uvozovkách je interpretována jako doslovný řetězec.

    a_star='a*'
    case $var in
      "$a_star") echo "'$var' is the two characters a, *";;
       $a_star) echo "'$var' begins with a";;
    esac
    
  • Ve dvojitých závorkách. Dvojité závorky jsou speciální syntaxí shellu.

    [[ -e $filename ]]
    

    Kromě toho, že potřebujete dvojité uvozovky tam, kde se očekává vzor nebo regulární výraz:na pravé straně = nebo == nebo != nebo =~ .

    a_star='a*'
    if [[ $var == "$a_star" ]]; then echo "'$var' is the two characters a, *"
    elif [[ $var == $a_star ]]; then echo "'$var' begins with a"
    fi
    

    Potřebujete dvojité uvozovky jako obvykle v jednoduchých závorkách [ … ] protože jde o běžnou syntaxi shellu (je to příkaz, který se náhodou nazývá [ ). Viz Jednoduché nebo dvojité závorky

  • V přesměrování v neinteraktivních shellech POSIX (nikoli bash , ani ksh88 ).

    echo "hello world" >$filename
    

    Některé shelly, když jsou interaktivní, zacházejí s hodnotou proměnné jako se zástupným vzorem. POSIX zakazuje toto chování v neinteraktivních shellech, ale několik shellů včetně bash (kromě režimu POSIX) a ksh88 (včetně případů, kdy je nalezen jako (údajně) POSIX sh některých komerčních Unices jako Solaris) to tam stále dělá (bash se také pokouší o rozdělení a přesměrování se nezdaří, pokud rozdělení+globování výsledkem je přesně jedno slovo), proto je lepší uvádět cíle přesměrování v sh skript pro případ, že jej chcete převést na bash skript jednoho dne, nebo jej spusťte na systému, kde je sh v tomto bodě nevyhovuje, nebo může pocházet z zdrojů z interaktivních shellů.

  • Uvnitř aritmetického výrazu. Ve skutečnosti musíte uvozovky vynechat, aby mohla být proměnná analyzována jako aritmetický výraz.

    expr=2*2
    echo "$(($expr))"
    

    Potřebujete však uvozovky kolem aritmetického rozšíření, protože ve většině shellů podléhají dělení slov, jak vyžaduje POSIX (!?).

  • V dolním indexu asociativního pole.

    typeset -A a
    i='foo bar*qux'
    a[foo bar*qux]=hello
    echo "${a[$i]}"
    

V některých vzácných případech může být užitečná proměnná a příkazy bez uvozovek:

  • Pokud se hodnota proměnné nebo výstup příkazu skládá ze seznamu vzorů glob a chcete tyto vzory rozšířit na seznam odpovídajících souborů.
  • Když víte, že hodnota neobsahuje žádný zástupný znak, $IFS nebyl změněn a chcete jej rozdělit na mezery.
  • Chcete-li rozdělit hodnotu na určitý znak:vypněte globování pomocí set -f , nastavte IFS na oddělovací znak (nebo jej ponechte tak, aby se rozdělil na mezery), pak proveďte rozšíření.
Související:Proč používat swap, když je v paměti RAM více než dostatek volného místa?

Zsh

V zsh můžete většinou až na pár výjimek vynechat dvojité uvozovky.

  • $var nikdy se nerozšíří na více slov, ale rozšíří se na prázdný seznam (na rozdíl od seznamu obsahujícího jediné prázdné slovo), pokud je hodnota var je prázdný řetězec. Kontrast:

    var=
    print -l $var foo        # prints just foo
    print -l "$var" foo      # prints an empty line, then foo
    

    Podobně "${array[@]}" expanduje na všechny prvky pole, zatímco $array expanduje pouze na neprázdné prvky.

  • @ příznak rozšíření parametru někdy vyžaduje uvozovky kolem celé náhrady:"${(@)foo}" .

  • Náhrada příkazu podstoupí rozdělení pole, pokud není uvedeno v uvozovkách:echo $(echo 'a'; echo '*') vypíše a * (s jednou mezerou) zatímco echo "$(echo 'a'; echo '*')" vytiskne neupravený dvouřádkový řetězec. Použijte "$(somecommand)" Chcete-li získat výstup příkazu v jediném slově, bez posledních řádků. Použijte "${$(somecommand; echo _)%?}" získat přesný výstup příkazu včetně posledních řádků. Použijte "${(@f)$(somecommand)}" získat pole řádků z výstupu příkazu.

Související:vysázet -A dává chybu ve skriptu?
Linux
  1. Proč se Tilda (~) nerozšíří uvnitř dvojitých uvozovek?

  2. Zástupné znaky uvnitř uvozovek?

  3. Zabalit příkaz, který obsahuje jednoduché a dvojité uvozovky pro jiný příkaz?

  1. Kdy použít Nohup?

  2. Uzavření vs. Neuvádíte hodnotu proměnné v uvozovkách v Bash?

  3. Proč se při použití uvozovek zobrazuje jedno zpětné lomítko?

  1. Kdy použít dedikovaný server

  2. Odstraňování jednoduchých a dvojitých uvozovek v řetězci pouze pomocí bash / standardních příkazů Linuxu

  3. Přidat dvojité uvozovky kolem polí ve výstupu skriptu AWK?