Care este diferența dintre operatorii Bash [[vs [vs (vs ((?

Sunt puțin confuz cu privire la ce fac acești operatori diferit atunci când folosit în bash (paranteze, paranteze duble, paranteză și paranteză dublă).

[[ , [ , ( , (( 

Am văzut că oamenii le folosesc dacă afirmații de genul acesta:

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

Comentarii

Răspundeți

În cochilii de tip Bourne, o declarație if arată de obicei ca

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

Clauza then este executată dacă codul de ieșire al lista de comenzi este zero. Dacă codul de ieșire este diferit de zero, atunci se execută clauza else. command-list1 poate fi simplu sau complex. Poate fi, de exemplu, o secvență de una sau mai multe conducte separate de unul dintre operatorii ;, &, &&, || sau linie nouă. Condițiile if prezentate mai jos sunt doar cazuri speciale de command-list1:

  1. if [ condition ]

    [ este un alt nume pentru comanda tradițională test. [ / test este un utilitar POSIX standard. Toate shell-urile POSIX îl au încorporat (deși „nu este necesar de POSIX²). Comanda test setează un cod de ieșire și declarația if acționează în consecință. Testele tipice sunt dacă există un fișier sau dacă un număr este egal cu altul.

  2. if [[ condition ]]

    Aceasta este o nouă variantă actualizată pentru test ¹ din ksh care bash , zsh , yash , busybox sh acceptă, de asemenea. Această construcție [[ ... ]] stabilește, de asemenea, un cod de ieșire și if acționează în consecință. Printre caracteristicile sale extinse, poate testa dacă un șir se potrivește cu un model cu caractere wildcard (nu în busybox sh ).

  3. if ((condition))

    O altă extensie ksh acceptată de bash și zsh . Aceasta efectuează aritmetica. Ca rezultat al aritmeticii, este setat un cod de ieșire și declarația if acționează rdingly. Returnează un cod de ieșire zero (adevărat) dacă rezultatul calculului aritmetic este diferit de zero. La fel ca [[...]], acest formular nu este POSIX și, prin urmare, nu este portabil.

  4. if (command)

    Aceasta execută comanda într-un sub-shell. Când comanda este finalizată, setează un cod de ieșire și declarația if acționează în consecință.

    Un motiv tipic pentru utilizarea unui subshell ca acesta este limitarea efectelor secundare ale command dacă command necesită atribuiri variabile sau alte modificări ale mediului shell-ului. Astfel de modificări nu rămân după finalizarea sub-shell-ului.

  5. if command

    comanda este executată și declarația if acționează conform codului său de ieșire.


¹ deși nu este într-adevăr o comandă, ci o construcție specială de shell cu propria sintaxă separată de cea a comenzii normale, și variază semnificativ între implementările shell

² POSIX necesită existența unui test și [ utilități pe sistem, totuși, deși în cazul [, mai multe distribuții Linux au fost cunoscute ca fiind m emiterea acestuia.

Comentarii

  • Vă mulțumim că ați inclus cea de-a 5-a opțiune. Aceasta ‘ este cheia pentru a înțelege cum funcționează efectiv și este surprinzător de subutilizată.
  • Rețineți că [ este binar, nu o comandă sau un simbol intern. În general locuiește în /bin.
  • @JulienR. de fapt [ este un built-in, la fel ca și test. Există versiuni binare disponibile din motive de compatibilitate. Verificați help [ și help test.
  • Merită menționat faptul că while ((isnt POSIX, $(( adică expansiunea aritmetică este ușoară și este ‘ confundată. Deseori, o soluție este să folosiți ceva de genul [ $((2+2)) -eq 4 ] pentru a folosi aritmetica în enunțurile conditinale
  • Aș vrea să votez în sus acest răspuns de mai multe ori. Explicație perfectă.

Răspuns

  • (…) paranteze indică un subshell . Ceea ce este în interiorul lor nu este o expresie ca în multe alte limbi. „Este o listă de comenzi (la fel ca parantezele exterioare). Aceste comenzi sunt executate într-un subproces separat, astfel încât orice redirecționare, atribuire etc. efectuată în interiorul parantezelor nu are efect în afara parantezelor.
    • Cu un semn de dolar, $(…) este o substituire de comandă : există o comandă în paranteze și ieșirea din comandă este folosit ca parte a liniei de comandă (după extinderi suplimentare, cu excepția cazului în care substituirea este între ghilimele duble, dar „s altă poveste ).
  • { … } paranteze sunt ca paranteze prin faptul că grupează comenzi, dar influențează doar analiza, nu gruparea. Programul x=2; { x=4; }; echo $x tipărește 4, în timp ce x=2; (x=4); echo $x tipărește 2. (De asemenea, parantezele care sunt cuvinte cheie trebuie delimitate și găsit în poziția de comandă (de aici spațiul după { și ; înainte de }) întrucât parantezele nu sunt „t. Asta” este doar o simplă sintaxă.)
    • Cu un semn de dolar principal, ${VAR} este un extinderea parametrilor , extinzându-se la valoarea unei variabile, cu posibile transformări suplimentare. Shell-ul ksh93 acceptă, de asemenea, ${ cmd;} ca formă de înlocuire a comenzilor care nu generează un subshell.
  • ((…)) paranteze duble înconjoară o instrucțiune aritmetică , adică un calcul pe numere întregi, cu o sintaxă care seamănă cu alte limbaje de programare. Această sintaxă este folosită mai ales pentru atribuiri și condiționare. Aceasta există doar în ksh / bash / zsh, nu în sh simplu.
    • Aceeași sintaxă este utilizată în expresiile aritmetice $((…)), care se extind la valoarea întreagă a expresiei.
  • [ … ] paranteze unice surround expresii condiționale . Expresiile condiționale sunt construite în mare parte pe operatori , cum ar fi -n "$variable" pentru a testa dacă o variabilă este goală și -e "$file" pentru a testa dacă există un fișier. Rețineți că aveți nevoie de un spațiu în jurul fiecăruia operator (de ex. [ "$x" = "$y" ], nu [ "$x"="$y" ] ) și un spațiu sau un caracter ca ; atât în interiorul, cât și în exteriorul parantezelor (de exemplu, [ -n "$foo" ], nu [-n "$foo"] ).
  • [[ … ]] parantezele duble sunt o formă alternativă de expresii condiționale în ksh / bash / zsh cu câteva caracteristici suplimentare, de exemplu puteți scrie [[ -L $file && -f $file ]] pentru a testa dacă un fișier este o legătură simbolică către un fișier obișnuit, în timp ce parantezele simple necesită [ -L "$file" ] && [ -f "$file" ]. Consultați De ce funcționează extinderea parametrilor cu spații fără ghilimele între paranteze duble [[dar nu paranteze simple [? ] pentru mai multe despre acest subiect.

În shell, comanda fiecare este o comandă condițională: fiecare comandă are o stare de returnare care este fie 0 indicând succesul, fie un număr întreg între 1 și 255 (și potențial mai mult în unele shell-uri) indicând eșec. Comanda [ … ] (sau forma de sintaxă [[ … ]]) este o comandă specială care poate fi scrisă și test … și reușește atunci când există un fișier sau când un șir nu este gol sau când un număr este mai mic decât altul etc. Forma de sintaxă ((…)) reușește atunci când un număr este zero .Iată câteva exemple de condiționare într-un script shell:

  • Testați dacă myfile conține șirul hello:

    if grep -q hello myfile; then … 
  • Dacă mydir este un director, schimbați în faceți și faceți lucruri:

    if cd mydir; then echo "Creating mydir/myfile" echo "some content" >myfile else echo >&2 "Fatal error. This script requires mydir to exist." fi 
  • Testați dacă există un fișier numit myfile în directorul curent:

    if [ -e myfile ]; then … 
  • La fel, dar inclusiv și legături simbolice suspendate:

    if [ -e myfile ] || [ -L myfile ]; then … 
  • Testați dacă valoarea x (care se presupune că este numerică) este de cel puțin 2, portabil:

    if [ "$x" -ge 2 ]; then … 
  • Testează dacă valoarea lui x (care se presupune că este numerică) este cel puțin 2, în bash / ksh / zsh:

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

Comentarii

  • Rețineți că un singur parantez acceptă -a în loc de &&, deci se poate scrie: [ -L $file -a -f $file ], care reprezintă același număr de caractere din paranteze fără [ și ]
  • @AlexisWilke Operatorii -a și -o sunt problematice deoarece pot duce la analize incorecte dacă unii dintre operanzii implicați par a fi operatori. ‘ de ce nu le menționez ‘: au un avantaj zero și nu au ‘ nu funcționează întotdeauna. Și nu scrieți niciodată expansiuni variabile necotate fără un motiv întemeiat: [[ -L $file -a -f $file ]] este bine, dar cu paranteze simple aveți nevoie de [ -L "$file" -a -f "$file" ] (ceea ce este ok, de exemplu, dacă $file începe întotdeauna cu / sau ./).
  • Rețineți că it ‘ s [[ -L $file && -f $file ]] (nu -a cu [[...]] variantă).

Răspuns

[ vs [[

Acest răspuns va acoperi [ vs [[ subset al întrebării.

Unele diferențe în Bash 4.3.11:

  • POSIX vs Bash extension:

  • comanda regulată vs magie

    • [ este doar o comandă obișnuită cu un nume ciudat.

      ] este doar ultimul argument al [.

    Ubuntu 16.04 are de fapt un executabil pentru acesta la /usr/bin/[ furnizat de coreutils , dar versiunea integrată bash are prioritate.

    Nimic nu este modificat în modul în care Bash analizează comanda.

    În special, < este redirecționare, && și || concatenează mai multe comenzi, ( ) generează sub-shell, cu excepția cazului în care a scăpat de \, iar extinderea cuvântului are loc ca de obicei.

    • [[ X ]] o singură construcție care face ca X să fie analizat magic. <, &&, || și () sunt tratate special, iar regulile de împărțire a cuvintelor sunt diferite.

      Există și alte diferențe, cum ar fi = și =~ .

    În basheză: [ este o comandă încorporată și [[ este un cuvânt cheie: https://askubuntu.com/questions/445749/whats-the-difference-between-shell-builtin-and-shell-keyword

  • <

  • && și ||

    • [[ a = a && b = b ]]: adevărat, logic și
    • [ a = a && b = b ]: eroare de sintaxă, && analizat ca separator de comenzi ȘI cmd1 && cmd2
    • [ a = a ] && [ b = b ]: POSIX echivalent de încredere
    • [ a = a -a b = b ]: aproape echivalent, dar depreciat de POSIX deoarece este nebun și nu reușește pentru unele valori a sau b cum ar fi ! sau ( care ar fi interpretat ca operații logice
  • (

    • [[ (a = a || a = b) && a = b ]]: fals. Fără ( ), ar fi adevărat deoarece [[ && ]] are o prioritate mai mare decât [[ || ]]
    • [ ( a = a ) ]: eroare de sintaxă, () este interpretată ca un subshell
    • [ \( a = a -o a = b \) -a a = b ]: echivalent, dar (), -a și -o sunt depreciate de POSIX. Fără \( \) ar fi adevărat deoarece -a are o prioritate mai mare decât -o
    • { [ a = a ] || [ a = b ]; } && [ a = b ] echivalent POSIX neacceptat. Cu toate acestea, în acest caz, am fi putut scrie doar: [ a = a ] || [ a = b ] && [ a = b ] deoarece || și && operatorii shell au prioritate egală spre deosebire de [[ || ]] și [[ && ]] și -o, -a și [
  • divizarea cuvintelor și generarea numelui de fișier la expansiuni (split + glob )

    • x="a b"; [[ $x = "a b" ]]: adevărat, ghilimelele nu sunt necesare
    • x="a b"; [ $x = "a b" ]: sintaxă eroare, se extinde la [ a b = "a b" ]
    • x="*"; [ $x = "a b" ]: eroare de sintaxă dacă există mai multe fișiere în directorul curent .
    • x="a b"; [ "$x" = "a b" ]: echivalent POSIX
  • =

    • [[ ab = a? ]]: adevărat, deoarece face potrivirea modelului ( * ? [ sunt magice). Nu se extinde la f se află în directorul curent.
    • [ ab = a? ]: a? glob se extinde. Deci, poate fi adevărat sau fals în funcție de fișierele din directorul curent.
    • [ ab = a\? ]: fals, nu expansiune glob
    • = și == sunt aceleași în ambele [ și [[, dar == este o extensie Bash.
    • case ab in (a?) echo match; esac: echivalent POSIX
    • [[ ab =~ "ab?" ]]: fals, pierde magia cu "" în Bash 3.2 și versiunile ulterioare și compatibilitatea cu bash 3.1 nu este activată (cum ar fi cu BASH_COMPAT=3.1)
    • [[ ab? =~ "ab?" ]]: adevărat
  • =~

    • [[ ab =~ ab? ]]: adevărat, POSIX extins expresie regulată se potrivește, ? nu se extinde glob
    • [ a =~ a ]: eroare de sintaxă. Fără echivalent bash.
    • printf "ab\n" | grep -Eq "ab?": echivalent POSIX (numai date cu o singură linie)
    • awk "BEGIN{exit !(ARGV[1] ~ ARGV[2])}" ab "ab?": Echivalent POSIX.

Recomandare: folosiți întotdeauna []

Există echivalente POSIX pentru fiecare [[ ]] construct pe care l-am văzut.

Dacă utilizați [[ ]], veți:

  • pierdeți portabilitatea
  • forțați cititorul să învețe complexitățile unei alte extensii bash. [ este doar o comandă obișnuită cu un nume ciudat, nu este implicată nicio semantică specială.

Mulțumită Stéphane Chazelas pentru corecții și completări importante.

Comentarii

  • @St é phaneChazelas mulțumesc pentru informații! Am ‘ am adăugat expr la răspuns. Termenul ” Bash ex tensiunea ” nu înseamnă că Bash a fost primul shell care a adăugat o sintaxă, învățarea POSIX sh vs Bash este deja suficientă pentru a mă înnebuni.
  • Vedeți man test dacă ați încercat man [ și v-ați pierdut. Aceasta va explica varianta POSIX.
  • Corecție: ] este un argument pentru comanda [, dar nu ‘ t împiedică folosirea altor argumente. ] trebuie să fie ultimul argument pentru [, dar poate apărea și ca parte a expresiei de testare. De exemplu, if [ "$foo" = ] ]; then va testa dacă variabila foo este setată la „] ” (la fel ca if [ ] = "$foo" ]; then).
  • @GordonDavisson mulțumesc, nu ‘ nu știu asta, fix.
  • @ tgm1024 – Monicawasm a tratat da, acesta este, de asemenea, o considerație valabilă.

Răspuns

Din documentație bash :

(list) lista este executată într-un mediu sub-shell (consultați MEDIUL DE EXECUTARE A COMANDEI mai jos). Atribuțiile variabile și comenzile integrate care afectează mediul shell nu rămân în vigoare după finalizarea comenzii. Starea de returnare este starea de ieșire a listei.

Cu alte cuvinte, vă asigurați că orice se întâmplă în „listă” (cum ar fi un cd) nu are niciun efect în afara ( și ). Singurul lucru care se va scurge este codul de ieșire al ultimei comenzi sau cu set -e prima comandă care generează o eroare (altele decât câteva, cum ar fi if, while etc.)

((expression)) Expresia este evaluată în conformitate cu regulile descrise mai jos în EVALUAREA ARITMETICĂ. Dacă valoarea expresiei este diferită de zero, starea de returnare este 0; în caz contrar, starea de returnare este 1. Acest lucru este exact echivalent cu ” expresie „.

Aceasta este o extensie bash care vă permite să faceți matematică. Acest lucru este oarecum similar cu utilizarea expr fără toate limitările expr (cum ar fi spații peste tot, evadarea * etc.)

[[ expression ]] Returnează o stare de 0 sau 1 în funcție de evaluarea expresiei condiționate a expresiei. Expresiile sunt compuse din elementele primare descrise mai jos în EXPRESII CONDIȚIONALE. Împărțirea cuvintelor și extinderea căii nu se efectuează pe cuvintele dintre [[și]]; se efectuează extinderea tildei, extinderea parametrilor și variabilelor, extinderea aritmetică, substituirea comenzilor, substituirea procesului și eliminarea citatelor. Operatorii condiționați, cum ar fi -f, trebuie să fie necitați pentru a fi recunoscuți ca primari.

Când se utilizează cu [[, < și > operatorii sortează lexicografic folosind locația curentă.

Acesta oferă un test avansat pentru a compara șiruri, numere și fișiere un pic ca test oferă, dar mai puternic.

[ expr ] Returnează o stare de 0 (adevărat) sau 1 (fals) în funcție de evaluarea expresiei condiționate expr. Fiecare operator și oper și trebuie să fie un argument separat. Expresiile sunt compuse din elementele primare descrise mai sus în EXPRESII CONDIȚIONALE. testul nu acceptă nicio opțiune și nici nu acceptă și ignoră un argument al – ca semn al sfârșitului opțiunilor.

[…]

Acesta apelează test. De fapt, pe vremuri, [ era o legătură simbolică către test. Funcționează la fel și aveți aceleași limitări. Deoarece un binar știe numele cu care a fost pornit, programul de testare știe când a fost pornit ca [ și își poate ignora ultimul parametru, care este de așteptat să fie ]. Trucuri Fun Unix.

Rețineți că în cazul bash, [ și test sunt funcții încorporate (așa cum se menționează într-un comentariu), totuși se aplică aproape aceleași limitări.

Comentarii

  • Deși test și [ sunt, desigur, comenzi integrate în Bash, dar este ‘ probabil că un Binarul extern există și el.
  • Binarul extern pentru [ nu este o legătură simbolică către test pe majoritatea sistemelor moderne .
  • Cumva, mi se pare amuzant că se deranjează să creeze două binare separate, care au ambele exact exact ceea ce au nevoie, în loc să le combine doar și să adauge câteva condiționale. Deși de fapt strings /usr/bin/test arată că are și textul de ajutor, așa că nu ‘ știu ce să spun.
  • @ Random832 Îmi dau seama despre raționamentul GNU pentru a evita un comportament neașteptat de arg0, dar despre cerințele POSIX, nu aș fi ‘ aș fi atât de afirmativ. În timp ce comanda test este evident necesară pentru a exista ca o comandă bazată pe fișier independentă de standard, nimic din aceasta nu menționează varianta [ să fie implementat și în acest fel. De exemplu, Solaris 11 nu ‘ nu oferă niciun executabil [, dar este totuși pe deplin conform cu standardele POSIX
  • (ieșirea 1) are un efect în afara parantezelor.

Răspuns

Câteva exemple:

Test tradițional:

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

test și [ sunt comenzi ca oricare altele, deci variabila este împărțită în cuvinte, cu excepția cazului în care este „între ghilimele.

Test în stil nou

[[ ... ]] este un (mai nou) construcție specială de shell, care funcționează puțin diferit, cel mai evident lucru fiind că nu are variabile divizate de cuvinte:

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

Unele documentație pe [ și [[ aici .

Test aritmetic:

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

„Normal „comenzi:

Toate cele de mai sus acționează ca niște comenzi normale și if poate lua orice comandă:

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

Comenzi multiple:

au putem folosi mai multe comenzi. Înfășurarea unui set de comenzi în ( ... ) le rulează în subshell, creând o copie temporară a stării shell-ului (director de lucru, variabile). Dacă avem nevoie pentru a rula temporar un program într-un alt director:

# 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 ... 

Răspuns

Comenzi de grupare

Bash oferă două moduri de a grupa o listă de comenzi care trebuie executate ca unitate.

( list ) Plasarea unei liste de comenzi între paranteze determină crearea unui mediu subshell și executarea fiecărei comenzi din listă în acel subshell. Deoarece lista este executate într-un subshell, atribuțiile variabile nu rămân în vigoare după finalizarea subshell-ului.

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

{ list; } Plasarea unui lista de comenzi între acolade face ca lista să fie executată în contextul curent al shell-ului . Nu este creată nicio sub-coajă. Este necesar următorul punct și virgulă (sau linia nouă). Sursă

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

Construcții condiționate

Single Bracket ie []
Pentru comparație ==, !=, <, și > și trebuie utilizat și pentru comparație numerică eq, ne,lt și gt ar trebui utilizat.

Suporturi îmbunătățite ie [[]]

În toate exemplele de mai sus, am folosit doar paranteze simple pentru a încadra expresia condițională, dar bash permite paranteze duble care servește ca o versiune îmbunătățită a sintaxei cu paranteză unică.

Pentru comparație, ==, !=, <, și > pot fi utilizate literalmente.

  • [ este un sinonim pentru comanda de testare. Chiar dacă este încorporat în shell creează un proces nou.
  • [[ este o nouă versiune îmbunătățită a acestuia, care este un cuvânt cheie, nu un program .
  • [[ este înțeles de Korn și Bash.

Sursă

Lasă un răspuns

Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *