Is test of [of [meer draagbaar, zowel tussen bash-shells als tussen andere shells?

Ik zie dat ik het kan doen

$ [ -w /home/durrantm ] && echo "writable"
writable

of

$ test -w /home/durrantm && echo "writable"
writable

of

$ [[ -w /home/durrantm ]] && echo "writable"
writable

I like using the third syntax. Are they equivalent in all ways and fof all negative and edge cases? Are there any differences in poftability, e.g. between bash on Ubuntu and on OS X of older/newer bash versions, e.g. befofe/after 4.0 and do they both expand expressions the same way?

42
Voor [...] versus [[...]] versus test ... zijn er meer volledige antwoorden in deze meestal dubbele vraag .
toegevoegd de auteur Valters Vingolds, de bron
Zie voor de specifieke kwestie van het testen op schrijfbaarheid van een bestand ook Hoe niet-invasief testen voor schrijftoegang tot een bestand?
toegevoegd de auteur G-Man, de bron
Er is een gezegde: "er is geen draagbare code, alleen code die geporteerd is". Mijn advies met betrekking tot dit: gebruik de meest leesbare vorm (waarschijnlijk [[...]]) en probeer het op alle platforms die u wilt ondersteunen. Er is weinig aan de hand om uw scripts te verdoezelen, zodat ze op oude platforms worden uitgevoerd die u noch uw doelgroep gebruikt. Het maakt je code gewoon moeilijk leesbaar, introduceert onnodige bugs en misschien zelfs beveiligingsproblemen (zoals bij openssl).
toegevoegd de auteur Sam Gamoran, de bron

7 antwoord

Ja, er zijn verschillen. De meest draagbare zijn test of [] . Deze zijn beide onderdeel van de POSIX test -specificatie .

Het construct if ... fi is ook gedefinieerd door POSIX en moet volledig draagbaar zijn.

De [[]] is een ksh -functie die ook voorkomt in sommige versies van bash ( alle moderne versies ) , in zsh en misschien in anderen, maar is niet aanwezig in sh of dash of de verschillende andere eenvoudigere shells.

Dus, om uw scripts draagbaar te maken, gebruikt u [] , test van als ... fi .

34
toegevoegd
Een interessant kenmerk van [[ is dat parameteruitbreidingen niet hoeven te worden geciteerd: [[- f $ bestand]] gebeurtenis werkt als $ bestand bevat witruimtetekens.
toegevoegd de auteur Matt Enright, de bron
@helpermethod ook, ingebouwde reguliere expressies [[$ a = ~ ^ reg. * exp. * $ ']]
toegevoegd de auteur GnP, de bron
Gewoon om op te merken, bash en zsh hebben [[] geruime tijd ondersteund ( bash heeft het toegevoegd in de eind jaren negentig, zsh niet later dan 2000, en ik zou verbaasd zijn als het ooit ontbrak aan ondersteuning), dus het is onwaarschijnlijk dat je een versie van een van beide tegenkomt zonder [[. Het bezoeken van een andere POSIX-compatibele shell (zoals dash ) is veel waarschijnlijker.
toegevoegd de auteur Luke Smith, de bron

[ is synonym of the test command and it is simultaneously a bash builtin and separate command. But [[ is a bash keyword and works in some versions only. So for reasons of portability you are better off using single [] or test

[ -w "/home/durrantm" ] && echo "writable"
28
toegevoegd

Please note, that [] && cmd Is niet hetzelfde alsif .. fi construction.

Sometimes its behaviour its pretty similar and you can use [] && cmd instead of if .. fi. But only sometimes. If you have more then one command to execute if condition or you need if .. else .. fi be careful and whatch the logic.

Een paar voorbeelden:

[ -z "$VAR" ] && ls file || echo wiiii

Is niet hetzelfde als

if [ -z $VAR ] ; then
  ls file
else
  echo wiii
fi

omdat als ls mislukt, echo wordt uitgevoerd, wat niet zal gebeuren met if .

Een ander voorbeeld:

[ -z "$VAR" ] && ls file && echo wiii

Is niet hetzelfde als

if [ -z "$VAR" ] ; then
   ls file
   echo $wiii
fi

hoewel deze constructie hetzelfde zal werken

[ -z "$VAR" ] && { ls file ; echo wiii ; }

let op ; na echo is belangrijk en moet daar zijn.

Dus bovenstaande uitspraak kan we zeggen

[] && cmd == if first command is successful then execute the next one

if .. fi == if condition (which may be the test command as well) then execute command(s)

Dus voor draagbaarheid tussen [ en [[ gebruik [ alleen.

if is POSIX compatible. So if you have to choose between [ and if choose looking at your task and expected behaviour.

19
toegevoegd
Ik ben downvoted omdat a) dit vooral een goed antwoord is, maar het is een antwoord op een andere vraag. b) u presenteert [ en als als subtitels, maar dat is niet het geval. Het is eigenlijk de && die de als vervangt. [ voert een code uit en retourneert een status, net zoals ls en grep zou doen. als takken worden uitgevoerd afhankelijk van de retourstatus van de opdracht (instructie) die wordt gegeven na de if, het kan een willekeurige opdracht zijn (instructie). && voert de volgende instructie alleen uit als de vorige 0 retourneerde, net als een eenvoudige if..then..fi .
toegevoegd de auteur GnP, de bron
@rush je hebt gelijk, ik heb de bewerkingsgeschiedenis daar gemist. Mijn verontschuldigingen. Zoals ik al zei, dit is vooral een goed antwoord, dus ik heb mijn stem verholpen.
toegevoegd de auteur GnP, de bron
Ik ben waarschijnlijk alleen maar dicht, maar ik zie niet wat het verschil zou zijn ... zou je alsjeblieft een voorbeeld kunnen geven waar die twee constructies verschillende resultaten zouden geven?
toegevoegd de auteur evilsoup, de bron
@evilsoup, bijgewerkt. Misschien ben ik niet de beste uitlegger, hoewel ik hoop dat het nu duidelijk zal zijn.
toegevoegd de auteur agrublev, de bron
@gnp, goed. a) bekijk commentaar van Michael. Er is een korte verklaring dat er aanvankelijk een andere vraag was. Ik heb zojuist het antwoord voor de geschiedenis achtergelaten. b) Het antwoord ging vooral over het verschil tussen && .. || en if .. else .. fi .
toegevoegd de auteur agrublev, de bron
Maakte de vraag over [vs [[vs-test en niet ook over als ... fi vs && om er één vraag van te maken. Helaas maakt dit het antwoord afwijkend. Excuses voor het niet krijgen van de vraag die in eerste instantie gericht was en die hiertoe leidde. Leef en (probeer te) leren. :)
toegevoegd de auteur ExpelledFromParadise, de bron
Zeer goede punten, haast. Ik veronderstel dat een veilige vorm van uw eerste voorbeeld zou zijn: [-z "$ VAR"] && {ls file; true; } || echo wiiii . Het is een beetje meer uitgebreid, maar het is nog steeds korter dan de if ... fi constructie.
toegevoegd de auteur DirkGently, de bron

It's actually the && that is replacing the if, not the test: an if statement in shell scripting tests whether a command returned a "successful" (zero) exit status; in your example, the command is [.

Er zijn dus twee dingen die je hier varieert: de opdracht die wordt gebruikt om de test uit te voeren en de syntaxis die wordt gebruikt om code uit te voeren op basis van het resultaat van die test.

Testopdrachten:

  • test is a standardised command for evaluating properties of strings and files; in your example, you are running the command test -w /home/durrantm
  • [ is an alias of that command, equally standardised, which has a mandatory last argument of ] in order to look like a bracketed expression; don't be fooled, it's still just a command (you may even find that your system has a file called /bin/[)
  • [[ is an extended version of the test command built into some shells, but not part of the same POSIX standard; it includes extra options which you are not using here

Voorwaardelijke uitdrukkingen:

  • The && operator (standardised here) performs a logical AND operation, by evaluating two commands and returning 0 (which represents true) if they both return 0; it will only evaluate the second command if the first one returned zero, so it can be used as a simple conditional
  • The if ... then ... fi construct (standardised here) uses the same method of judging "truth", but allows for a compound list of statements in the then clause, rather than the single command afforded by an && short-circuit, and provides elif and else clauses, which are hard to write using only && and ||. Note that there are no brackets around the condition in an if statement.

Dus, de volgende zijn allemaal even draagbare, en volledig gelijkwaardige weergaven van uw voorbeeld:

  • test -w /home/durrantm && echo "writable"
  • [ -w /home/durrantm ] && echo "writable"
  • if test -w /home/durrantm; then echo "writable"; fi
  • if [ -w /home/durrantm ]; then echo "writable"; fi

Hoewel het volgende ook gelijkwaardig, maar minder draagbaar is vanwege het afwijkende karakter van [[:

  • [[ -w /home/durrantm ]] && echo "writable"
  • if [[ -w /home/durrantm ]]; then echo "writable"; fi
9
toegevoegd
Ja, ik heb het if .... fi-gedeelte verwijderd om dit 1 vraag te laten zijn.
toegevoegd de auteur ExpelledFromParadise, de bron

Als je draagbaarheid wilt buiten de Bourne-achtige wereld, dan:

test -w /home/durrantm && echo writable

is het meest draagbaar. Het werkt in shells van de families Bourne, csh en rc .

test -w /home/durrantm && echo "writable"

zou "beschrijfbaar" uitvoeren in plaats van beschrijfbaar in shells van de rc familie ( rc , es , akanga , waarbij " niet speciaal is).

[ -w /home/durrantm ] && echo writable

zou niet werken in shells van de families csh of rc op systemen die geen [ -opdracht in $ PATH hebben code> (van sommigen is bekend dat ze test hebben maar niet de [ alias).

if [ -w /home/durrantm ]; then echo writabe; fi

werkt alleen in schelpen van de familie Bourne.

[[ -w /home/durrantm ]] && echo writable

werkt alleen in ksh (waar het oorspronkelijk vandaan kwam), zsh en bash (alle 3 in de Bourne familie).

Geen zou werken in de shell fish waar u nodig hebt:

[ -w /home/durrantm ]; and echo writable

of:

if [ -w /home/durrantm ]; echo writable; end
7
toegevoegd

Voor draagbaarheid gebruikt u test / [. Maar als u de draagbaarheid niet nodig hebt, gebruik dan [[] voor het gezond verstand van uzelf en anderen die uw script lezen. :)

Zie ook Wat is het verschil tussen test, [en [[? in de BashFAQ .

7
toegevoegd

My most important reason for choosing either if foo; then bar; fi or foo && bar is whether the exit status of the whole command is important.

vergelijken:

#!/bin/sh
set -e
foo && bar
do_baz

met:

#!/bin/sh
set -e
if foo; then bar; fi
do_baz

Je zou kunnen denken dat ze hetzelfde doen; als foo echter faalt (of is, afhankelijk van uw standpunt, onjuist), wordt do_baz in het eerste voorbeeld niet uitgevoerd, omdat het script is afgesloten ... De set -e geeft de shell opdracht om onmiddellijk af te sluiten als een opdracht een valse status retourneert. Heel handig als je dingen doet als:

cd /some/directory
rm -rf *

U wilt niet dat het script blijft werken als de cd om welke reden dan ook faalt.

0
toegevoegd
Geen enkele mislukte foo zal het script in beide gevallen niet afbreken. Dat is een speciaal geval voor set -e (wanneer de opdracht wordt geëvalueerd als een voorwaarde (aan de linkerkant van sommige &&/|| of in if/while/until/elsif ... voorwaarden). A falen bar zou de shell in beide gevallen verlaten.
toegevoegd de auteur Stéphane Chazelas, de bron
U wilt cd/some/directory && rm -rf - * of cd/some/directory || Uitgang; rm -rf - * (verwijdert nog steeds verborgen bestanden niet). Persoonlijk vind ik het niet prettig om set -e te gebruiken als excuus om geen moeite te doen om de juiste code te schrijven.
toegevoegd de auteur Stéphane Chazelas, de bron