Verwendung von doppelten oder einfachen Klammern, Klammern und geschweiften Klammern

657

Ich bin verwirrt über die Verwendung von Klammern, Klammern, geschweiften Klammern in Bash sowie den Unterschied zwischen ihren Doppel- oder Einzelformen. Gibt es eine klare Erklärung?

Tim
quelle

Antworten:

605

In Bash testund [sind Shell Builtins.

Die doppelte Klammer , bei der es sich um ein Shell-Schlüsselwort handelt, ermöglicht zusätzliche Funktionen. Beispielsweise können Sie &&und ||anstelle von -aund verwenden, -ound es gibt einen Operator für die Übereinstimmung regulärer Ausdrücke =~.

In einem einfachen Test scheinen doppelte eckige Klammern viel schneller zu bewerten als einzelne.

$ time for ((i=0; i<10000000; i++)); do [[ "$i" = 1000 ]]; done

real    0m24.548s
user    0m24.337s
sys 0m0.036s
$ time for ((i=0; i<10000000; i++)); do [ "$i" = 1000 ]; done

real    0m33.478s
user    0m33.478s
sys 0m0.000s

Die geschweiften Klammern werden zusätzlich zur Begrenzung eines Variablennamens für die Parametererweiterung verwendet, damit Sie Folgendes tun können:

  • Schneiden Sie den Inhalt einer Variablen ab

    $ var="abcde"; echo ${var%d*}
    abc
  • Nehmen Sie ähnliche Ersetzungen vor wie sed

    $ var="abcde"; echo ${var/de/12}
    abc12
  • Verwenden Sie einen Standardwert

    $ default="hello"; unset var; echo ${var:-$default}
    hello
  • und einige mehr

Durch Klammererweiterungen werden außerdem Listen mit Zeichenfolgen erstellt, die normalerweise in Schleifen durchlaufen werden:

$ echo f{oo,ee,a}d
food feed fad

$ mv error.log{,.OLD}
(error.log is renamed to error.log.OLD because the brace expression
expands to "mv error.log error.log.OLD")

$ for num in {000..2}; do echo "$num"; done
000
001
002

$ echo {00..8..2}
00 02 04 06 08

$ echo {D..T..4}
D H L P T

Beachten Sie, dass die führenden Null- und Inkrementfunktionen vor Bash 4 nicht verfügbar waren.

Vielen Dank an gboffi, der mich an Klammererweiterungen erinnert hat.

Doppelte Klammern werden für arithmetische Operationen verwendet :

((a++))

((meaning = 42))

for ((i=0; i<10; i++))

echo $((a + b + (14 * c)))

Mit ihnen können Sie die Dollarzeichen für Ganzzahl- und Arrayvariablen weglassen und zur besseren Lesbarkeit Leerzeichen um Operatoren einfügen.

Einzelne Klammern werden auch für Array- Indizes verwendet:

array[4]="hello"

element=${array[index]}

Für (die meisten / alle?) Array-Referenzen auf der rechten Seite ist eine geschweifte Klammer erforderlich.

Der Kommentar von Ephemient erinnerte mich daran, dass Klammern auch für Unterschalen verwendet werden. Und dass sie verwendet werden, um Arrays zu erstellen.

array=(1 2 3)
echo ${array[1]}
2
Bis auf weiteres angehalten.
quelle
8
WARNUNG: Diese Funktion ist eine Gabelbombe. Führen Sie sie nicht aus. Siehe: en.wikipedia.org/wiki/Fork_bomb
Bis auf weiteres angehalten.
3
Es ist nur eine Gabelbombe, wenn Sie sie mit einer zusätzlichen aufrufen :.
Ephemient
7
Der Vollständigkeit halber bin ich gerade in einem alten Skript darauf gestoßen : $[expression]; Dies ist die alte, veraltete arithmetische Ausdruckssyntax für die neuere, bevorzugte Syntax:$((expression))
Michael
2
@DennisWilliamson Eine andere Verwendung von geschweiften Klammern in bashist das Erstellen von Sequenzen, wie unten peripher erwähnt ( stackoverflow.com/a/8552128/2749397 ). Da ich diese Funktion etwas kommentieren möchte (wie Sie es nicht erwähnt haben ;-) I ' Ich nehme mir die Freiheit, die am häufigsten gewählte Antwort als Vehikel zu verwenden ... Zwei Beispiele für Sequenzliterale: echo {01..12}-> 01 02 03 04 05 06 07 08 09 10 11 12(beachten Sie die anfängliche Null); echo {C..Q}-> C D E F G H I J K L M N O P Q. Seine Hauptverwendung ist in Schleifen, zB for cnt in {01..12} ; do ... ${cnt} ... ; done
gboffi
1
@gboffi: Die Funktion zum Auffüllen von Nullen wurde in Bash 4 verfügbar. Außerdem können Sie in Bash 4 ein Inkrement in einer Sequenz angeben: echo {01..12..2}-> "01 03 05 07 09 11". Danke für die Erinnerung an Sequenzen. Ich werde es meiner Antwort hinzufügen.
Bis auf weiteres angehalten.
335
  1. Eine einzelne Klammer ( [) ruft normalerweise ein Programm mit dem Namen auf [. man testoder man [für weitere Infos. Beispiel:

    $ VARIABLE=abcdef
    $ if [ $VARIABLE == abcdef ] ; then echo yes ; else echo no ; fi
    yes
  2. Die doppelte Klammer ( [[) macht (im Grunde) dasselbe wie eine einzelne Klammer, ist jedoch eine eingebaute Bash.

    $ VARIABLE=abcdef
    $ if [[ $VARIABLE == 123456 ]] ; then echo yes ; else echo no ; fi
    no
  3. Klammern ( ()) werden verwendet, um eine Unterschale zu erstellen. Zum Beispiel:

    $ pwd
    /home/user 
    $ (cd /tmp; pwd)
    /tmp
    $ pwd
    /home/user

    Wie Sie sehen können, konnten Sie mit der Subshell Vorgänge ausführen, ohne die Umgebung der aktuellen Shell zu beeinträchtigen.

  4. (a) Klammern ( {}) werden verwendet, um Variablen eindeutig zu identifizieren. Beispiel:

    $ VARIABLE=abcdef
    $ echo Variable: $VARIABLE
    Variable: abcdef
    $ echo Variable: $VARIABLE123456
    Variable:
    $ echo Variable: ${VARIABLE}123456
    Variable: abcdef123456

    (b) Klammern werden auch verwendet, um eine Folge von Befehlen im aktuellen Shell-Kontext auszuführen , z

    $ { date; top -b -n1 | head ; } >logfile 
    # 'date' and 'top' output are concatenated, 
    # could be useful sometimes to hunt for a top loader )
    
    $ { date; make 2>&1; date; } | tee logfile
    # now we can calculate the duration of a build from the logfile

Es gibt jedoch einen subtilen syntaktischen Unterschied zu ( )(siehe Bash-Referenz ); im Wesentlichen, ein Semikolon ;nach dem letzten Befehl in Klammern ist ein Muss, und die Streben {, } müssen durch Leerzeichen umgeben sein.

Carl Norum
quelle
26
Nun, [ist eigentlich ein eingebautes in Bash, aber es soll sich /bin/[im Gegensatz zum [[eingebauten verhalten. [[hat verschiedene Funktionen, wie logischere Operationen und verschiedene Anführungszeichenrollen. Zusätzlich: Einzelne Klammern werden auch für Arrays, Prozesssubstitution und erweiterte Globs verwendet. Doppelklammern werden für die Arithmetik verwendet; Geschweifte Klammern {}werden für die Befehlsgruppierung oder eine Vielzahl von Arten der Parametererweiterung oder Klammererweiterung oder Sequenzerweiterung verwendet. Ich bin sicher, ich habe auch einige andere Anwendungen verpasst ...
Ephemient
4
Die Doppelgleichheit im Ausdruck if [ $VARIABLE == abcdef ]ist ein Bashismus, der - obwohl er funktioniert - wahrscheinlich vermieden werden sollte; Verwenden Sie entweder explizit bash ( if [[ ...==...]]) oder machen Sie deutlich, dass Sie die traditionellere Bedingung ( if [ "$VARIABLE" = "abcdef" ]) verwenden. Wahrscheinlich sollten Skripte so einfach und portabel wie möglich beginnen, bis sie wirklich spezifische Funktionen für Bash benötigen (aus dem einen oder anderen Grund). Aber auf jeden Fall sollte die Absicht klar sein; "=" und "==" und "[[" und "[" funktionieren unterschiedlich und ihre Verwendung sollte konsistent sein.
Michael
3
@michael_n: +1 für diese Bemerkung. Nebenbei bemerkt, ich liebe Skripte, aber ich finde es ziemlich umständlich, dass die tragbare Methode darin besteht, über [ "$var" = ".."]statt zu testen ==, während sie in C anstelle von Tests zugewiesen wird (und eine häufige Ursache für Fehler ist) ... warum nicht nicht testverwenden ==statt =? weiß jemand?
Olivier Dulac
Auch hier ist eine lustige Sache, dass (zumindest auf Kubuntu) der Befehl /usr/bin/[kein Symlink zu dem ist /usr/bin/test, und mehr noch: Diese Programme haben sogar ein paar verschiedene Größen!
Hi-Angel
Außerdem: Eine einzelne schließende Klammer )ist Teil der caseAnweisungssyntax zum Beenden einer Fallzeile. Es hat keine öffnende Klammer. Das warf mich vom ersten Mal ab, als ich es sah.
Agustín Amenabar
302

Klammern

if [ CONDITION ]    Test construct  
if [[ CONDITION ]]  Extended test construct  
Array[1]=element1   Array initialization  
[a-z]               Range of characters within a Regular Expression
$[ expression ]     A non-standard & obsolete version of $(( expression )) [1]

[1] http://wiki.bash-hackers.org/scripting/obsolete

Geschweifte Klammern

${variable}                             Parameter substitution  
${!variable}                            Indirect variable reference  
{ command1; command2; . . . commandN; } Block of code  
{string1,string2,string3,...}           Brace expansion  
{a..z}                                  Extended brace expansion  
{}                                      Text replacement, after find and xargs

Klammern

( command1; command2 )             Command group executed within a subshell  
Array=(element1 element2 element3) Array initialization  
result=$(COMMAND)                  Command substitution, new style  
>(COMMAND)                         Process substitution  
<(COMMAND)                         Process substitution 

Doppelte Klammern

(( var = 78 ))            Integer arithmetic   
var=$(( 20 + 5 ))         Integer arithmetic, with variable assignment   
(( var++ ))               C-style variable increment   
(( var-- ))               C-style variable decrement   
(( var0 = var1<98?9:21 )) C-style ternary operation
Yola
quelle
@ Yola, kannst du bitte erklären, was $ (varname) genau bedeutet? In Apple Xcode-Projekten kann ich Dateipfade als Skripteingabe / -ausgabe angeben. Wenn ich $ SRC_ROOT / myFile.txt oder $ {SRC_ROOT} /myFile.txt angeben (SRC_ROOT var wird vom Build-System exportiert) - funktioniert nicht. Nur $ (SRC_ROOT) /myFile.txt funktioniert. Was könnte der Grund sein? klar var name ist kein befehl?
Motti Shneor
1
@MottiShneor, in Ihrem Fall $(varname)hat nichts mit der Bash-Syntax zu tun . Es ist Teil der Makefile-Syntax .
Sasha
Nicht so - Xcode erstellt nicht mit Makefiles und seine Variablen sind Umgebungsvariablen. Proprietäre Xcode-Build-System-Prozesse lesen die Werte dieser vordefinierten Umgebungsvariablen. Benutzerdefinierte Build-Schritte sind nur normale Shell-Skripte (Bash oder andere) und haben Zugriff auf dieselben Variablen.
Motti Shneor
@MottiShneor, ok, lass uns verfeinern: höchstwahrscheinlich ist es Teil der xcconfig-Syntax . Wie auch immer, $(varname)hat in Ihrem Fall keine Beziehung zur Bash-Syntax.
Sasha
Sie erwähnen nicht, was der Unterschied zwischen dem Testkonstrukt und dem erweiterten Testkonstrukt ist.
Nikos
23

Ich wollte nur diese von TLDP hinzufügen :

~:$ echo $SHELL
/bin/bash

~:$ echo ${#SHELL}
9

~:$ ARRAY=(one two three)

~:$ echo ${#ARRAY}
3

~:$ echo ${TEST:-test}
test

~:$ echo $TEST


~:$ export TEST=a_string

~:$ echo ${TEST:-test}
a_string

~:$ echo ${TEST2:-$TEST}
a_string

~:$ echo $TEST2


~:$ echo ${TEST2:=$TEST}
a_string

~:$ echo $TEST2
a_string

~:$ export STRING="thisisaverylongname"

~:$ echo ${STRING:4}
isaverylongname

~:$ echo ${STRING:6:5}
avery

~:$ echo ${ARRAY[*]}
one two one three one four

~:$ echo ${ARRAY[*]#one}
two three four

~:$ echo ${ARRAY[*]#t}
one wo one hree one four

~:$ echo ${ARRAY[*]#t*}
one wo one hree one four

~:$ echo ${ARRAY[*]##t*}
one one one four

~:$ echo $STRING
thisisaverylongname

~:$ echo ${STRING%name}
thisisaverylong

~:$ echo ${STRING/name/string}
thisisaverylongstring
kzh
quelle
18
Denken Sie daran, dass echo ${#ARRAY}drei angezeigt werden, weil das erste Element von ARRAYdrei Zeichen enthält, nicht weil es drei Elemente enthält! Verwenden Sie zum Drucken der Anzahl der Elemente echo ${#ARRAY[@]}.
TrueY
@zeal ist ${TEST:-test}gleich, $TESTwenn die Variable TESTexistiert, andernfalls wird einfach die Zeichenfolge "test" zurückgegeben. Es gibt eine andere Version, die noch mehr kann: ${TEST:=test}--- was auch gleich ist, $TESTwenn TEST existiert, aber wenn dies nicht der Fall ist, erstellt sie die Variable TESTund weist einen Wert "test" zu und wird auch zum Wert des gesamten Ausdrucks.
Liebt Wahrscheinlichkeit
18

Der Unterschied zwischen Test , [ und [[ in großen Details in der erklärt BashFAQ .

Um es kurz zu machen: test implementiert die alte, portable Syntax des Befehls. In fast allen Schalen (die ältesten Bourne-Schalen sind die Ausnahme) ist [ein Synonym für Test (erfordert jedoch ein letztes Argument von]). Obwohl alle modernen Shells integrierte Implementierungen von [haben, gibt es normalerweise immer noch eine externe ausführbare Datei mit diesem Namen, z. B. / bin / [.

[[ist eine neue verbesserte Version davon, bei der es sich um ein Schlüsselwort und nicht um ein Programm handelt. Dies hat vorteilhafte Auswirkungen auf die Benutzerfreundlichkeit, wie unten gezeigt. [[wird von KornShell und BASH (z. B. 2.03) verstanden, nicht jedoch von der älteren POSIX oder BourneShell.

Und das Fazit:

Wann sollte der neue Testbefehl verwendet werden [[und wann der alte [? Wenn die Portabilität auf die BourneShell ein Problem darstellt, sollte die alte Syntax verwendet werden. Wenn das Skript andererseits BASH oder KornShell erfordert, ist die neue Syntax viel flexibler.

fwhacking
quelle
18

Klammern in der Funktionsdefinition

()Bei der Funktionsdefinition werden Klammern verwendet:

function_name () { command1 ; command2 ; }

Aus diesem Grund müssen Sie auch in Befehlsparametern Klammern vermeiden:

$ echo (
bash: syntax error near unexpected token `newline'

$ echo \(
(

$ echo () { command echo The command echo was redefined. ; }
$ echo anything
The command echo was redefined.
Pabouk
quelle
Oh, ich habe es mit dem csh versucht. Mein Fehler. Wenn ich Bash einprobiere, funktioniert es. Ich kannte den Befehl 'Befehl' von Bash nicht.
Chan Kim
Wie kann ich die Neudefinition des Befehls echo () abbrechen? (ohne die Bash wieder zu öffnen)
Chan Kim
2
@ChanKim : unset -f echo. Siehe help unset.
Pabouk
0
Truncate the contents of a variable

$ var="abcde"; echo ${var%d*}
abc

Make substitutions similar to sed

$ var="abcde"; echo ${var/de/12}
abc12

Use a default value

$ default="hello"; unset var; echo ${var:-$default}
hello
vuppala srikar
quelle
Wenn Sie eine Frage beantworten, zielen Sie nicht nur auf "Code", sondern versuchen Sie auch, eine Erklärung hinzuzufügen ...
Mikev