Como usar colchetes duplos ou simples, parênteses, chaves


658

Estou confuso com o uso de colchetes, parênteses, chaves no Bash, bem como a diferença entre as formas duplas ou simples. Existe uma explicação clara?

Respostas:


606

No Bash, teste [são shell shell.

O colchete duplo , que é uma palavra-chave shell, permite funcionalidade adicional. Por exemplo, você pode usar &&e ||, em vez de -ae -oe há um operador de expressão correspondência normal =~.

Além disso, em um teste simples, colchetes duplos parecem avaliar muito mais rápido que os únicos.

$ 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

Os colchetes, além de delimitar um nome de variável, são usados ​​para a expansão de parâmetros, para que você possa fazer coisas como:

  • Truncar o conteúdo de uma variável

    $ var="abcde"; echo ${var%d*}
    abc
    
  • Faça substituições semelhantes a sed

    $ var="abcde"; echo ${var/de/12}
    abc12
    
  • Use um valor padrão

    $ default="hello"; unset var; echo ${var:-$default}
    hello
    
  • e vários mais

Além disso, as expansões de chaves criam listas de cadeias que são iteradas em loops:

$ 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

Observe que os principais recursos de zero e incremento não estavam disponíveis antes do Bash 4.

Obrigado a gboffi por me lembrar sobre expansões de braçadeira.

Parênteses duplos são usados ​​para operações aritméticas :

((a++))

((meaning = 42))

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

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

e eles permitem que você omita os sinais de dólar nas variáveis ​​de número inteiro e matriz e inclua espaços em torno dos operadores para facilitar a leitura.

Parênteses únicos também são usados ​​para índices de matriz :

array[4]="hello"

element=${array[index]}

A cinta encaracolada é necessária para (a maioria / todas?) Referências de matriz no lado direito.

O comentário de ephemient me lembrou que os parênteses também são usados ​​para subcamadas. E que eles são usados ​​para criar matrizes.

array=(1 2 3)
echo ${array[1]}
2

8
AVISO: Essa função é uma bomba de forquilha, não a execute. Consulte: en.wikipedia.org/wiki/Fork_bomb
pausado até novo aviso.

3
É apenas uma bomba de bifurcação se você invocá-la com um adicional :.
ephemient

7
Também para completar, eu só vim através deste em um script de idade: $[expression]; esta é a sintaxe antiga, obsoleta da expressão aritmética para a sintaxe preferida mais recente:$((expression))
michael

2
@DennisWilliamson Outro uso de chaves na bashcriação de seqüências é a criação de seqüências, como periférico mencionado abaixo ( stackoverflow.com/a/8552128/2749397 ) Como eu gostaria de comentar um pouco esse recurso (como você não mencionou ;-) Eu ' m tendo a liberdade de usar a resposta mais votada como veículo ... Dois exemplos de literais de sequência: echo {01..12}-> 01 02 03 04 05 06 07 08 09 10 11 12(observe o zero inicial); echo {C..Q}-> C D E F G H I J K L M N O P Q. Seu principal uso é em loops, por exemplo, for cnt in {01..12} ; do ... ${cnt} ... ; done
gboffi

1
@gboffi: o recurso de preenchimento zero ficou disponível no Bash 4. Além disso, no Bash 4, você pode especificar um incremento em uma sequência: echo {01..12..2}-> "01 03 05 07 09 11". Obrigado pelo lembrete sobre sequências. Vou adicioná-lo à minha resposta.
Pausado até novo aviso.

336
  1. Um único colchete ( [) geralmente chama um programa chamado [; man testou man [para mais informações. Exemplo:

    $ VARIABLE=abcdef
    $ if [ $VARIABLE == abcdef ] ; then echo yes ; else echo no ; fi
    yes
    
  2. O colchete duplo ( [[) faz a mesma coisa (basicamente) que um colchete único, mas é um bash embutido.

    $ VARIABLE=abcdef
    $ if [[ $VARIABLE == 123456 ]] ; then echo yes ; else echo no ; fi
    no
    
  3. Parênteses ( ()) são usados ​​para criar um subshell. Por exemplo:

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

    Como você pode ver, o subshell permitiu executar operações sem afetar o ambiente do shell atual.

  4. (a) Chaves ( {}) são usadas para identificar inequivocamente variáveis. Exemplo:

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

    (b) Chaves também são usadas para executar uma sequência de comandos no contexto atual do shell, por exemplo

    $ { 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
    

Existe uma diferença sintática sutil com ( ), no entanto (consulte a referência do bash ); essencialmente, um ponto e vírgula ;após o último comando dentro de chaves é uma obrigação, e as chaves {, } deve ser cercado por espaços.


26
Bem, [na verdade é um built-in no Bash, mas deve agir como um /bin/[oposto ao [[built-in. [[possui recursos diferentes, como operações mais lógicas e funções de cotação diferentes. Além disso: parênteses únicos também são usados ​​para matrizes, substituição de processos e globs estendidos; parênteses duplos são usados ​​para aritmética; chaves {}são usadas para agrupamento de comandos ou multidões de tipos de expansão de parâmetro ou expansão de chave ou expansão de sequência. Estou certo de que eu perdi alguns outros usos também ...
ephemient

4
O duplo-igual na expressão if [ $VARIABLE == abcdef ]é um basismo que - embora funcione - provavelmente deve ser evitado; use explicitamente bash ( if [[ ...==...]]) ou deixe claro que você está usando a condicional ( if [ "$VARIABLE" = "abcdef" ]) mais tradicional . Indiscutivelmente, os scripts devem começar o mais simples e portátil possível, até que eles realmente precisem de recursos específicos para o bash (por um motivo ou outro). Mas, em qualquer caso, a intenção deve ser clara; "=" e "==" e "[[" e "[") funcionam de maneira diferente e seu uso deve ser consistente.
6136 Michael

3
@michael_n: +1 para esta observação. Em uma nota lateral, eu amo scripts, mas acho bastante estranho que a maneira portátil seja testar via em [ "$var" = ".."]vez de ==, enquanto em C ela atribuiria em vez de testar (e é uma causa bastante comum de bugs) ... por que não 't testusar ==em vez de =? Alguém sabe?
Olivier Dulac 30/10

Também aqui está uma coisa engraçada que (pelo menos no Kubuntu) o comando /usr/bin/[não é um link simbólico para o /usr/bin/teste mais: esses programas têm até alguns tamanhos diferentes!
Hi-Angel

Além disso: um único parêntese de fechamento )faz parte da casesintaxe da instrução para finalizar uma linha de caso. Não possui parênteses de abertura. Isso me assustou na primeira vez que o vi.
Agustín Amenabar

302

Suportes

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

Aparelho Encaracolado

${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

Parênteses

( 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 

Parênteses duplos

(( 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, você poderia explicar o que $ (varname) exatamente? Nos projetos Apple Xcode, posso especificar caminhos de arquivo como entrada / saída de script. se eu especificar $ SRC_ROOT / myFile.txt ou $ {SRC_ROOT} /myFile.txt (SRC_ROOT var é exportado pelo sistema de compilação) - não funciona. somente $ (SRC_ROOT) /myFile.txt funciona. Qual seria a razão? claramente nome var não é um comando?
Motti Shneor 31/10/19

1
@MottiShneor, no seu caso $(varname)não está relacionado à sintaxe do bash. Faz parte da sintaxe Makefile .
Sasha

Não é assim - o Xcode não está construindo usando makefile e suas variáveis ​​são variáveis ​​de ambiente. Os processos proprietários do sistema de construção do Xcode lêem os valores dessas variáveis ​​de ambiente predefinidas. As etapas de compilação personalizadas são apenas scripts shell normais (bash ou outro) e têm acesso aos mesmos vars.
Motti Shneor

@ MottiShneor, ok, vamos refinar: provavelmente faz parte da sintaxe do xcconfig . De qualquer forma, $(varname)não tem relação com a sintaxe do bash no seu caso.
Sasha

Você não menciona qual é a diferença entre a construção de teste e a construção de teste estendido.
Nikos

23

Eu só queria adicionar estes do TLDP :

~:$ 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

18
Mente que echo ${#ARRAY}exibe três, por causa do primeiro elemento do ARRAYcontém três caracteres, não porque ele contém três elementos! Para imprimir o número de elementos, use echo ${#ARRAY[@]}.
TrueY 01/08/19

@zeal ${TEST:-test}é igual $TESTse a variável TESTexistir, caso contrário, simplesmente retorna a string "test". Existe outra versão que faz ainda mais: ${TEST:=test}--- que também é igual a $TESTse TEST existe, mas sempre que não existe, ela cria a variável TESTe atribui um valor "test" e também se torna o valor de toda a expressão.
Ama Probabilidade

18

A diferença entre teste , [ e [[ é explicado em grandes detalhes no BashFAQ .

Para resumir uma longa história: test implementa a sintaxe antiga e portátil do comando. Em quase todos os shells (os shells Bourne mais antigos são a exceção), [é sinônimo de teste (mas requer um argumento final de]). Embora todos os shells modernos tenham implementações internas de [, geralmente ainda existe um executável externo com esse nome, por exemplo, / bin / [.

[[é uma nova versão aprimorada, que é uma palavra-chave, não um programa. Isso tem efeitos benéficos na facilidade de uso, como mostrado abaixo. [[é entendido por KornShell e BASH (por exemplo, 2.03), mas não pelo POSIX ou BourneShell mais antigo.

E a conclusão:

Quando o novo comando de teste [[deve ser usado e quando o antigo [? Se a portabilidade para o BourneShell for uma preocupação, a sintaxe antiga deve ser usada. Se, por outro lado, o script exigir BASH ou KornShell, a nova sintaxe será muito mais flexível.


18

Parênteses na definição de função

Parênteses ()estão sendo usados ​​na definição de função:

function_name () { command1 ; command2 ; }

Essa é a razão pela qual você precisa escapar parênteses, mesmo nos parâmetros de comando:

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

$ echo \(
(

$ echo () { command echo The command echo was redefined. ; }
$ echo anything
The command echo was redefined.

Oh, eu tentei no csh. Foi mal. Quando tento bash, funciona. Eu não conhecia o comando 'comando' do bash.
Chan Kim

como posso cancelar a redefinição do comando echo ()? (sem reabrir a festa)
Chan Kim

2
@ChanKim: unset -f echo. Veja 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

Quando você responder a uma pergunta, não basta apontar para "código", mas também tentar adicionar uma explicação ...
Mikev
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.