Hvad er forskellen på Bash-operatorerne [[vs [vs (vs ((?

Jeg er lidt forvirret over, hvad gør disse operatører forskelligt, når brugt i bash (parentes, dobbelt parentes, parentes og dobbelt parentes).

[[ , [ , ( , (( 

Jeg har set folk bruge dem på, hvis udsagn som dette:

if [[condition]] if [condition] if ((condition)) if (condition) 

Kommentarer

Svar

I Bourne-lignende skaller ser en if udsagn typisk ud

if command-list1 then command-list2 else command-list3 fi 

then -klausulen udføres, hvis udgangskoden til listen over kommandoer er nul. Hvis udgangskoden ikke er nul, udføres else -sætningen. command-list1 kan være enkel eller kompleks. Det kan for eksempel være en sekvens af en eller flere rørledninger adskilt af en af operatørerne ;, &, &&, || eller newline. if betingelser vist nedenfor er kun specielle tilfælde af command-list1:

  1. if [ condition ]

    [ er et andet navn til den traditionelle test kommando. [ / test er et standard POSIX-værktøj. Alle POSIX-skaller har det indbygget (selvom det “ikke kræves af POSIX²). test -kommandoen indstiller en udgangskode og if -erklæringen handler i overensstemmelse hermed. Typiske tests er, om der findes en fil eller et nummer er lig med et andet.

  2. if [[ condition ]]

    Dette er en ny opgraderet variation på test ¹ fra ksh at bash , zsh , yash , busybox sh understøtter også. Denne [[ ... ]] -konstruktion indstiller også en udgangskode, og if udsagn fungerer i overensstemmelse hermed. Blandt de udvidede funktioner kan den teste, om en streng matcher et wildcard-mønster (ikke i busybox sh ).

  3. if ((condition))

    En anden ksh udvidelse, som bash og zsh også understøtter. Dette udfører aritmetik. Som resultat af aritmetikken indstilles en udgangskode, og if sætningen fungerer i overensstemmelse med rdingly. Det returnerer en udgangskode på nul (sand), hvis resultatet af den aritmetiske beregning ikke er nul. Ligesom [[...]] er denne formular ikke POSIX og derfor ikke bærbar.

  4. if (command)

    Dette kører kommando i en subshell. Når kommandoen er afsluttet, indstiller den en exit-kode, og if -sætningen fungerer i overensstemmelse hermed.

    En typisk grund til at bruge en subshell som denne er at begrænse bivirkninger command hvis command krævede variable tildelinger eller andre ændringer i shell-miljøet. Sådanne ændringer forbliver ikke, når subshell er afsluttet.

  5. if command

    kommandoen udføres, og if -erklæringen fungerer i henhold til dens udgangskode.


¹ dog ikke rigtig en kommando, men en speciel skalkonstruktion med sin egen separate syntaks fra den normale kommando, og varierer markant mellem shellimplementeringer

² POSIX kræver ikke, at der er en enkeltstående test og [ hjælpeprogrammer på systemet, men i tilfælde af [ har flere Linux-distributioner været kendt for at være m udsteder det.

Kommentarer

  • Tak, fordi du medtager den 5. mulighed. At ‘ er nøglen til at forstå, hvordan dette rent faktisk fungerer og overraskende er underudnyttet.
  • Bemærk, at [ faktisk er en binær, ikke en intern kommando eller et symbol. Bor generelt i /bin.
  • @JulienR. faktisk er [ en indbygget, ligesom test. Der findes binære versioner af kompatibilitetsårsager. Tjek help [ og help test.
  • Værd at bemærke, at mens ((ikke POSIX, $(( dvs. aritmetisk udvidelse er, og det ‘ er let at forvirre dem. Ofte er en løsning at bruge noget som [ $((2+2)) -eq 4 ] for at gøre brug af aritmetik i betingelsesudtalelser
  • Jeg ville ønske, at jeg kunne stemme over dette svar mere end én gang. Perfekt forklaring.

Svar

  • (…) parenteser angiver en subshell . Hvad der er inde i dem, er ikke et udtryk som på mange andre sprog. Det er en liste over kommandoer (ligesom uden for parenteser). Disse kommandoer udføres i en separat underproces, så enhver omdirigering, tildeling osv., Der udføres inden for parenteser, har ingen effekt uden for parenteserne.
    • Med en førende dollartegn, $(…) er en kommandosubstitution : der er en kommando inden for parenteserne, og output fra kommandoen bruges som en del af kommandolinjen (efter ekstra udvidelser, medmindre erstatningen er mellem dobbelt anførselstegn, men at “s en anden historie ).
  • { … } seler er som parenteser, fordi de grupperer kommandoer, men de påvirker kun parsing, ikke gruppering. Programmet x=2; { x=4; }; echo $x udskriver 4, mens x=2; (x=4); echo $x udskriver 2. (Også seler, der er nøgleord , skal afgrænses og findes i kommandoposition (deraf mellemrummet efter { og ; før }) hvorimod parenteser gør ikke det. Det er bare en syntaks-sære.)
    • Med et førende dollartegn er ${VAR} et parameterudvidelse , udvidet til værdien af en variabel med mulige ekstra transformationer. ksh93 shell understøtter også ${ cmd;} som en form for kommandosubstitution, der ikke skaber en subshell.
  • ((…)) dobbelte parenteser omgiver en aritmetisk instruktion , det vil sige en beregning på heltal med en syntaks, der ligner andre programmeringssprog. Denne syntaks bruges mest til opgaver og i betingede forhold. Denne findes kun i ksh / bash / zsh, ikke i almindelig sh.
    • Den samme syntaks bruges i aritmetiske udtryk $((…)), der udvides til udtrykets heltalsværdi.
  • [ … ] enkelt parenteser betingede udtryk . Betingede udtryk er for det meste bygget på operatorer såsom -n "$variable" for at teste, om en variabel er tom og -e "$file" for at teste, om der findes en fil. Bemærk, at du har brug for et mellemrum omkring hver operatør (f.eks. [ "$x" = "$y" ], ikke [ "$x"="$y" ] ), og et mellemrum eller et tegn som ; både inden for og uden for parenteserne (f.eks. [ -n "$foo" ], ikke [-n "$foo"] ).
  • [[ … ]] dobbelte parenteser er en alternativ form for betingede udtryk i ksh / bash / zsh med et par ekstra funktioner, for eksempel kan du skrive [[ -L $file && -f $file ]] for at teste, om en fil er et symbolsk link til en almindelig fil, mens enkelte parenteser kræver [ -L "$file" ] && [ -f "$file" ]. Se Hvorfor fungerer parameterudvidelse med mellemrum uden anførselstegn inden for dobbelt parentes [[men ikke enkelt parentes [? for mere om dette emne.

I skallen er hver kommando en betinget kommando: hver kommando har en returstatus, der enten er 0, der angiver succes eller et heltal mellem 1 og 255 (og potentielt mere i nogle skaller), der angiver fiasko. Kommandoen [ … ] (eller [[ … ]] syntaksform) er en bestemt kommando, som også kan staves test … og lykkes, når en fil findes, eller når en streng ikke er tom, eller når et nummer er mindre end en anden osv. ((…)) syntaksformularen lykkes, når et tal ikke er nul .Her er et par eksempler på betingelser i et shell-script:

  • Test om myfile indeholder strengen hello:

    if grep -q hello myfile; then … 
  • Hvis mydir er en mappe, skal du ændre til det og gør ting:

    if cd mydir; then echo "Creating mydir/myfile" echo "some content" >myfile else echo >&2 "Fatal error. This script requires mydir to exist." fi 
  • Test om der er en fil med navnet myfile i den aktuelle mappe:

    if [ -e myfile ]; then … 
  • Det samme, men også med dinglende symbolske links:

    if [ -e myfile ] || [ -L myfile ]; then … 
  • Test om værdien af x (som antages at være numerisk) er mindst 2, bærbart:

    if [ "$x" -ge 2 ]; then … 
  • Test om værdien af x (som antages at være numerisk) er mindst 2 i bash / ksh / zsh:

    if ((x >= 2)); then … 

Kommentarer

  • Bemærk, at en enkelt parentes understøtter -a i stedet for &&, så man kan skrive: [ -L $file -a -f $file ], hvilket er det samme antal tegn inden for parenteserne uden det ekstra [ og ]
  • @AlexisWilke Operatorerne -a og -o er problematisk, fordi de kan føre til forkerte parser, hvis nogle af de involverede operander ligner operatører. At ‘ hvorfor jeg ikke ‘ ikke nævner dem: de har ingen fordel og don ‘ fungerer ikke altid. Og skriv aldrig ikke-citerede variable udvidelser uden en god grund: [[ -L $file -a -f $file ]] er fint, men med enkelte parenteser har du brug for [ -L "$file" -a -f "$file" ] (hvilket er ok f.eks. Hvis $file starter altid med / eller ./).
  • Bemærk at det ‘ s [[ -L $file && -f $file ]] (ingen -a med [[...]] variant).

Svar

[ vs [[

Dette svar dækker [ vs [[ undersæt af spørgsmålet.

Nogle forskelle på Bash 4.3.11:

  • POSIX vs Bash-udvidelse:

  • regelmæssig kommando vs magi

    • [ er bare en almindelig kommando med et underligt navn.

      ] er bare det sidste argument for [.

    Ubuntu 16.04 har faktisk en eksekverbar fil til den på /usr/bin/[ leveret af coreutils , men den indbyggede bash-version har forrang.

    Intet ændres på den måde, Bash analyserer kommandoen.

    Især < er omdirigering, && og || sammenkæder flere kommandoer, ( ) genererer underskaller, medmindre de undslippes af \, og ordudvidelsen sker som normalt.

    • [[ X ]] er en enkelt konstruktion, der gør X til at blive analyseret magisk. <, &&, || og () behandles specielt, og orddelingsregler er forskellige.

      Der er også yderligere forskelle som = og =~ .

    I Bashese: [ er en indbygget kommando, og [[ er et nøgleord: https://askubuntu.com/questions/445749/whats-the-difference-between-shell-builtin-and-shell-keyword

  • <

  • && og ||

    • [[ a = a && b = b ]]: sand, logisk og
    • [ a = a && b = b ]: syntaksfejl, && parset som AND-kommandoseparator cmd1 && cmd2
    • [ a = a ] && [ b = b ]: POSIX pålidelig ækvivalent
    • [ a = a -a b = b ]: næsten ækvivalent, men udfaset af POSIX, fordi det er sindssygt og mislykkes for nogle værdier af a eller b som ! eller ( som vil blive fortolket som logiske operationer
  • (

    • [[ (a = a || a = b) && a = b ]]: falsk. Uden ( ) ville det være sandt, fordi [[ && ]] har større forrang end [[ || ]]
    • [ ( a = a ) ]: syntaksfejl, () fortolkes som en subshell
    • [ \( a = a -o a = b \) -a a = b ]: ækvivalent, men (), -a og -o udfases af POSIX. Uden \( \) ville være sandt, fordi -a har større forrang end -o
    • { [ a = a ] || [ a = b ]; } && [ a = b ] POSIX-ækvivalent, der ikke er udfaset. I dette særlige tilfælde kunne vi dog bare have skrevet: [ a = a ] || [ a = b ] && [ a = b ] fordi || og && shelloperatorer har samme forrang i modsætning til [[ || ]] og [[ && ]] og -o, -a og [
  • opdeling af ord og generering af filnavn ved udvidelser (split + glob )

    • x="a b"; [[ $x = "a b" ]]: sandt, citater ikke nødvendige
    • x="a b"; [ $x = "a b" ]: syntaks fejl, udvides til [ a b = "a b" ]
    • x="*"; [ $x = "a b" ]: syntaksfejl, hvis der er mere end en fil i den aktuelle bibliotek .
    • x="a b"; [ "$x" = "a b" ]: POSIX-ækvivalent
  • =

    • [[ ab = a? ]]: sandt, fordi det mønstermatchning ( * ? [ er magiske). Udvides ikke glob til f iles i den aktuelle mappe.
    • [ ab = a? ]: a? glob udvides. Så det kan være sandt eller forkert afhængigt af filerne i den aktuelle mappe.
    • [ ab = a\? ]: falsk, ikke globudvidelse
    • = og == er de samme i både [ og [[, men == er en Bash-udvidelse.
    • case ab in (a?) echo match; esac: POSIX-ækvivalent
    • [[ ab =~ "ab?" ]]: falsk, mister magi med "" i Bash 3.2 og derover og forudsat, at kompatibilitet med bash 3.1 ikke er aktiveret (som med BASH_COMPAT=3.1)
    • [[ ab? =~ "ab?" ]]: sand
  • =~

    • [[ ab =~ ab? ]]: true, POSIX udvidet match med regulært udtryk , ? udvides ikke glob
    • [ a =~ a ]: syntaksfejl. Ingen bash-ækvivalent.
    • printf "ab\n" | grep -Eq "ab?": POSIX-ækvivalent (kun enkeltlinjedata)
    • awk "BEGIN{exit !(ARGV[1] ~ ARGV[2])}" ab "ab?": POSIX-ækvivalent.

Anbefaling: brug altid []

Der er POSIX-ækvivalenter for hver [[ ]] konstruktion, jeg har set.

Hvis du bruger [[ ]], skal du:

  • miste bærbarhed
  • tvinge læseren til at lære indviklingen i en anden bash-udvidelse. [ er bare en almindelig kommando med et underligt navn, ingen speciel semantik er involveret.

Takket være Stéphane Chazelas for vigtige rettelser og tilføjelser.

Kommentarer

  • @St é phaneChazelas tak for informationen! Jeg ‘ har føjet expr til svaret. Udtrykket ” Bash ex spænding ” er ikke beregnet til at antyde, at Bash var den første skal, der tilføjede nogle syntaks. At lære POSIX sh vs Bash er allerede nok til at gøre mig skør.
  • Se man test hvis du prøvede man [ og gik vild. Det vil forklare POSIX-varianten.
  • Rettelse: ] er et argument til [ kommandoen, men det er ikke ‘ forhindrer ikke yderligere argumenter i at blive brugt. ] skal være sidste argument til [, men det kan også forekomme som en del af testudtrykket. For eksempel vil if [ "$foo" = ] ]; then teste, om variablen foo er indstillet til “] ” (som if [ ] = "$foo" ]; then).
  • @GordonDavisson tak, jeg ‘ t ved det, fast.
  • @ tgm1024 – Monicawasmbehandlet ja, det er også en gyldig overvejelse.

Svar

Fra bash-dokumentation :

(list) -listen udføres i et subshell-miljø (se COMMAND EXECUTION MILJØ nedenfor). Variable tildelinger og indbyggede kommandoer, der påvirker shell-miljøet, forbliver ikke i kraft, når kommandoen er afsluttet. Returstatus er udgangsstatus for listen. > Med andre ord sørger du for, at hvad som helst der sker i “liste” (som en cd) ikke har nogen virkning uden for ( og ). Det eneste, der lækker, er udgangskoden for den sidste kommando eller med set -e den første kommando, der genererer en fejl (anden end nogle få som if, while osv.)

((expression)) Udtrykket evalueres i henhold til reglerne beskrevet nedenfor under ARITMETISK EVALUERING. Hvis værdien af udtrykket ikke er nul, er returstatus 0, ellers er returstatus er 1. Dette svarer nøjagtigt til at lade ” udtryk “.

Dette er en bash-udvidelse, der giver dig mulighed for at lave matematik. Dette ligner noget at bruge expr uden alle begrænsningerne ved expr (såsom at have mellemrum overalt, undslippe * osv.)

[[ expression ]] Returner en status på 0 eller 1 afhængigt af evaluering af det betingede udtryk. Udtryk er sammensat af de primærvalg, der er beskrevet nedenfor under BETINGELSESUDTRYK. Opdeling af ord og udvidelse af sti udføres ikke på ordene mellem [[og]]; tildeudvidelse, parameter- og variabeludvidelse, aritmetisk udvidelse, kommandosubstitution, procesudskiftning og fjernelse af tilbud udføres. Betingede operatorer som -f skal ikke citeres for at blive anerkendt som primærvalg.

Når det bruges med [[, < og > operatorer sorterer leksikografisk ved hjælp af den aktuelle lokalitet.

Dette giver en avanceret test til at sammenligne strenge, tal og filer lidt som test tilbud, men mere kraftfuld.

[ expr ] Returner status på 0 (true) eller 1 (false) afhængigt af evalueringen af det betingede udtryk ekspr. Hver operatør og operatør skal være et separat argument. Udtryk er sammensat af de primærvalg, der er beskrevet ovenfor under KONDITIONELLE UDTRYK. test accepterer ikke nogen indstillinger, og accepterer ikke og ignorerer heller ikke et argument af – som betegnelse for afslutningen på indstillinger.

[…]

Denne kalder test. I gamle dage var [ faktisk et symbolsk link til test. Det fungerer på samme måde, og du har de samme begrænsninger. Da en binær kender navnet, som den blev startet med, ved testprogrammet, hvornår den blev startet som [, og den kan ignorere dens sidste parameter, som forventes at være ]. Sjove Unix-tricks.

Bemærk, at i tilfælde af bash, [ og test er indbyggede funktioner (som nævnt i en kommentar), men stort set de samme begrænsninger gælder.

Kommentarer

  • Selvom test og [ er selvfølgelig indbyggede kommandoer i Bash, men det ‘ er sandsynligt, at Ekstern binær findes også.
  • Den eksterne binære for [ er ikke et symbolsk link til test på de fleste moderne systemer .
  • På en eller anden måde finder jeg det morsomt, at de gider at oprette to separate binære filer, som begge har lige præcis, hvad de har brug for, i stedet for bare at kombinere dem og tilføje et par betingede. Selvom strings /usr/bin/test faktisk viser, at den også har hjælpeteksten, så jeg ved ikke ‘ hvad jeg skal sige.
  • @ Random832 Jeg får din mening om GNU-begrundelsen for at undgå uventet arg0-opførsel, men om POSIX-krav ville jeg ikke ‘ ikke være så bekræftende. Mens kommandoen test naturligvis kræves for at eksistere som en enkeltstående filbaseret kommando af standarden, angiver intet i den, at dens [ -variant skal implementeres på den måde også. For eksempel giver Solaris 11 ikke ‘ t nogen [ eksekverbar, men er ikke desto mindre fuldt ud kompatibel med POSIX-standarderne
  • (exit 1) har en virkning uden for parenteserne.

Svar

Nogle eksempler:

Traditionel test:

foo="some thing" # check if value of foo is not empty if [ -n "$foo" ] ; then... if test -n "$foo" ; then... 

test og [ er kommandoer som alle andre, så variablen er opdelt i ord, medmindre den er i anførselstegn.

Test i ny stil

[[ ... ]] er (nyere) speciel skalkonstruktion, der fungerer lidt anderledes, det mest åbenlyse er, at det ikke betyder ordopdelte variabler:

if [[ -n $foo ]] ; then... 

Nogle dokumentation om [ og [[ her .

Aritmetisk test:

foo=12 bar=3 if (( $foo + $bar == 15 )) ; then ... 

“Normal “kommandoer:

Alt det ovenstående fungerer som normale kommandoer, og if kan tage en hvilken som helst kommando:

# grep returns true if it finds something if grep pattern file ; then ... 

Flere kommandoer:

Eller vi kan bruge flere kommandoer. Indpakning af et sæt kommandoer i ( ... ) kører dem i subshell og opretter en midlertidig kopi af shellens tilstand (arbejdskatalog, variabler). at køre et program midlertidigt i en anden mappe:

# this will move to $somedir only for the duration of the subshell if ( cd $somedir ; some_test ) ; then ... # while here, the rest of the script will see the new working # directory, even after the test if cd $somedir ; some_test ; then ... 

Svar

Grupperingskommandoer

Bash giver to måder at gruppere en liste over kommandoer, der skal udføres som en enhed.

( list ) Ved at placere en liste med kommandoer mellem parenteser oprettes der et subshell-miljø, og hver af kommandoerne i listen udføres i den subshell. Da listen er udført i en subshell forbliver variable tildelinger ikke gældende, når subshell er afsluttet.

$ a=1; (a=2; echo "inside: a=$a"); echo "outside: a=$a" inside: a=2 outside: a=1 

{ list; } Placering af en liste over kommandoer mellem krøllede parenteser får listen til at blive udført i nuværende shell-kontekst . Ingen subshell oprettes. Semikolon (eller ny linje) efterfølgende liste er påkrævet. Kilde

${} Parameter expansion Ex: ANIMAL=duck; echo One $ANIMAL, two ${ANIMAL}s $() Command substitution Ex: result=$(COMMAND) $(()) Arithmetic expansion Ex: var=$(( 20 + 5 )) 

Betingede konstruktioner

Enkelt beslag dvs. []
Til sammenligning ==, !=, <, og > og skal bruges og til numerisk sammenligning eq, ne,lt og gt skal bruges.

Forbedrede parenteser dvs. [[]]

I alle ovenstående eksempler brugte vi kun enkelt parenteser til at omslutte det betingede udtryk, men bash tillader dobbelte parenteser, der fungerer som en forbedret version af syntaksen med enkelt parentes.

Til sammenligning kan ==, !=, <, og > bruge bogstaveligt.

  • [ er et synonym for testkommando. Selvom den er indbygget i skallen, skaber den en ny proces.
  • [[ er en ny forbedret version af den, som er et nøgleord, ikke et program .
  • [[ forstås ved Korn og Bash.

Kilde

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *