it-swarm-eu.dev

Proč rozšíření parametrů s mezerami bez uvozovek funguje uvnitř dvojitých závorek "[[", ale nikoli uvnitř jednotlivých závorek "["?

Jsem zmatený s použitím jednoduchých nebo dvojitých závorek. Podívejte se na tento kód:

dir="/home/mazimi/VirtualBox VMs"

if [[ -d ${dir} ]]; then
    echo "yep"
fi

Funguje to perfektně, i když řetězec obsahuje mezeru. Ale když to změním na jednu závorku:

dir="/home/mazimi/VirtualBox VMs"

if [ -d ${dir} ]; then
    echo "yep"
fi

Říká:

./script.sh: line 5: [: /home/mazimi/VirtualBox: binary operator expected

Když to změním na:

dir="/home/mazimi/VirtualBox VMs"

if [ -d "${dir}" ]; then
    echo "yep"
fi

Funguje to dobře. Může někdo vysvětlit, co se děje? Kdy bych měl přiřadit dvojité uvozovky kolem proměnných jako "${var}" předcházet problémům způsobeným mezerami?

90
Majid Azimi

Jednotná závorka [ je vlastně alias pro příkaz test, je to ne syntaxe.

Jednou z nevýhod (mnoha) jedné závorky je, že pokud jeden nebo více operandů se snaží vyhodnotit návrat prázdného řetězce, bude si stěžovat, že očekával dva operandy (binární). To je důvod, proč vidíte lidi dělat [ x$foo = x$blah ], x zaručuje, že operand nikdy nevyhodnotí prázdný řetězec.

Dvojitá závorka [[ ]], na druhé straně je syntaxe a je mnohem schopnější než [ ]. Jak jste zjistili, nemá problém s jediným operandem a umožňuje také více syntaxe typu C s >, <, >=, <=, !=, ==, &&, || operátory.

Moje doporučení je následující: Pokud je váš tlumočník #!/bin/bash, pak vždy použijte [[ ]]

Je důležité si uvědomit, že [[ ]] není podporován všemi POSIX shelly, nicméně mnoho shellů ho podporuje, jako je zsh a ksh kromě bash

88
SiegeX

Příkaz [ Je běžný příkaz. Ačkoli většina skořápek poskytuje to jako vestavěný pro účinnost, dodržuje Shell normální syntaktická pravidla. [ Je přesně ekvivalentní s test, kromě toho, že [ Vyžaduje jako svůj poslední argument ] A test nikoli.

Dvojitá závorka [[ … ]] Jsou speciální syntaxe. Byly zavedeny v ksh (několik let po [), Protože [ Může být obtížné správně používat a [[ Umožňuje některé nové přírůstky, které používají speciální znaky Shell. Například můžete psát

[[ $x = foo && $y = bar ]]

protože celý podmíněný výraz je analyzován Shellem, zatímco [ $x = foo && $y = bar ] by nejprve bylo rozděleno na dva příkazy [ $x = foo a $y = bar ] oddělených operátorem &&. Podobně dvojité závorky umožňují věci, jako je syntaxe odpovídající vzoru, např. [[ $x == a* ]] A otestujte, zda hodnota x začíná a; v samostatných závorkách by se to rozšířilo a* na seznam souborů, jejichž jména začínají a v aktuálním adresáři. Dvojité závorky byly poprvé představeny v ksh a jsou k dispozici pouze v ksh, bash a zsh.

V jednoduchých závorkách musíte použít dvojité uvozovky kolem proměnných substitucí, jako na většině jiných míst, protože jsou to jen argumenty příkazu (což se stane příkazem [). Uvnitř dvojitých závorek nepotřebujete dvojité uvozovky, protože prostředí Shell nerozděluje ani neroztříští slovo: analyzuje podmíněný výraz, nikoli příkaz.

Výjimkou však je [[ $var1 = "$var2" ]], Kde potřebujete uvozovky, pokud chcete provést porovnání řetězců bajt-bajt, jinak by $var2 Byl vzorem, který by odpovídal $var1 Proti .

Jedna věc, kterou nemůžete udělat s [[ … ]], Je použití proměnné jako operátora. Například je to naprosto legální (ale zřídka užitečné):

if [ -n "$reverse_sort" ]; then op=-gt; else op=-lt; fi
…
if [ "$x" "$op" "$y" ]; then …

Ve vašem příkladu

dir="/home/mazimi/VirtualBox VMs"
if [ -d ${dir} ]; then …

příkaz uvnitř if je [ se 4 argumenty -d, /home/mazimi/VirtualBox, VMs a ]. Shell analyzuje -d /home/mazimi/VirtualBox A pak neví, co dělat s VMs. Chcete-li získat dobře vytvořený příkaz, musíte zabránit rozdělování slov na ${dir}.

Obecně řečeno, vždy používejte dvojité uvozovky kolem proměnných a příkazových substitucí, pokud nevíte, že chcete na výsledku provést rozdělení slov a globping. Hlavní místa, kde je bezpečné nepoužívat dvojité uvozovky, jsou:

  • v přiřazení: foo=$bar (ale všimněte si, že potřebujete dvojité uvozovky v export "foo=$bar" nebo v přiřazeních polí jako array=("$a" "$b"));
  • v příkazu case: case $foo in …;
  • uvnitř dvojitých závorek, s výjimkou na pravé straně operátora = nebo == (pokud nechcete, aby odpovídala vzoru): [[ $x = "$y" ]].

Ve všech těchto je správné použít dvojité uvozovky, takže můžete také přeskočit pokročilá pravidla a používat uvozovky po celou dobu.

Kdy bych měl přiřadit dvojité uvozovky kolem proměnných jako "${var}" předcházet problémům způsobeným mezerami?

Implicitní v této otázce je

Proč není ${variable_name} dostatečně dobrý?

${variable_name} neznamená, co si myslíte, že to dělá ...

… Pokud si myslíte, že má cokoli co do činění s problémy způsobenými mezerami (v proměnných hodnotách). ${variable_name} je pro to dobré:

$ bar=foo
$ bard=Shakespeare
$ echo $bard
Shakespeare
$ echo ${bar}d
food

a nic jiného! 1${variable_name} nedělá žádné dobré, pokud jej bezprostředně nesledujete znakem, který by mohl být součástí názvu proměnné: písmeno (A-Z nebo a-z), podtržítko (_) nebo číslice (0-9). A i tak to můžete obejít:

$ echo "$bar"d
food

Nesnažím se odrazovat od jeho používání - echo "${bar}d" je pravděpodobně tím nejlepším řešením - ale odradit lidi od spoléhání se na složené závorky namísto uvozovek nebo instinktivně používat závorky a poté se zeptat: „Nyní potřebuji citace, také? Vždy byste měli používat uvozovky, pokud nemáte dobrý důvod, proč tomu tak není, a jste si jisti, že vědět, co děláte.
_________________
1Samozřejmě kromě toho, že fantaskní formy rozšíření parametr , například ${parameter:-[Word]} a ${parameter%[Word]}, stavět na ${parameter} syntaxe. Musíte také použít ${10}, ${11} atd. k odkazu na 10., 11. a další, poziční parametry - nabídky vám s tím nepomohou.

Pro manipulaci s mezerami a prázdnými | speciálními znaky v proměnných byste je měli vždy obklopovat dvojitými uvozovkami. Dobrá praxe je také nastavení správného IFS.

Doporučeno: http://www.dwheeler.com/essays/filenames-in-Shell.html

2
ramonovski