Qual é a diferença entre os operadores Bash [[vs [vs (vs ((?

171

Estou um pouco confuso sobre o que esses operadores fazem de maneira diferente quando usados em bash (parênteses, colchetes duplos, parênteses e parênteses duplos).

[[ , [ , ( , ((

Eu já vi pessoas usá-los em declarações como esta:

if [[condition]]

if [condition]

if ((condition))

if (condition)
    
por RetroCode 27.08.2016 / 20:43

6 respostas

180

Uma declaração if normalmente se parece com

if commands1
then
   commands2
else
   commands3
fi

A cláusula then é executada se o código de saída de commands1 for zero. Se o código de saída for diferente de zero, a cláusula else será executada. commands1 pode ser simples ou complexo. Pode, por exemplo, ser uma sequência de um ou mais pipelines separados por um dos operadores ; , & , && ou || . As condições if mostradas abaixo são apenas casos especiais de commands1 :

  1. if [ condition ]

    Este é o comando tradicional shell test . Está disponível em todos os shells do POSIX. O comando test define um código de saída e a instrução if age de acordo. Testes típicos são se um arquivo existe ou um número é igual a outro.

  2. if [[ condition ]]

    Esta é uma nova variação atualizada em test de ksh que bash e zsh também suportam. Esse comando test também define um código de saída e a instrução if age de acordo. Entre seus recursos estendidos, ele pode testar se uma string corresponde a uma expressão regular.

  3. if ((condition))

    Outra extensão ksh que bash e zsh também suporta. Isso realiza aritmética. Como resultado da aritmética, um código de saída é definido e a instrução if age de acordo. Retorna um código de saída de zero (verdadeiro) se o resultado do cálculo aritmético for diferente de zero. Como [[...]] , este formulário não é POSIX e, portanto, não é portável.

  4. if (command)

    Isso executa o comando em um subshell. Quando o comando é concluído, ele define um código de saída e a instrução if age de acordo.

    Uma razão típica para o uso de um subshell como esse é limitar os efeitos colaterais de command se command exigir atribuições de variáveis ou outras alterações no ambiente do shell. Tais mudanças não permanecem depois que a subshell é concluída.

  5. if command

    O comando

    é executado e a declaração if age de acordo com seu código de saída.

por 27.08.2016 / 21:07
60
  • (…) parênteses indicam um subnível . O que está dentro deles não é uma expressão como em muitos outros idiomas. É uma lista de comandos (assim como parênteses externos). Esses comandos são executados em um subprocesso separado, portanto, qualquer redirecionamento, atribuição, etc. executado dentro dos parênteses não tem efeito fora dos parênteses.
    • Com um cifrão, $(…) é uma substituição de comando : existe um comando dentro dos parênteses, e a saída do comando é usada como parte da linha de comando (depois de expansões extras a menos que a substituição esteja entre aspas duplas, mas isso é outra história ).
  • { … } chaves são parênteses, pois agrupam comandos, mas influenciam apenas a análise, não o agrupamento. O programa x=2; { x=4; }; echo $x imprime 4, enquanto x=2; (x=4); echo $x imprime 2. (Também chaves sendo palavras-chave precisam ser delimitadas e encontradas na posição de comando (daí o espaço após { e ; antes } ) enquanto os parênteses não. Isso é apenas uma peculiaridade de sintaxe.
    • Com um cifrão, ${VAR} é uma expansão de parâmetro , expandindo para o valor de uma variável, com possíveis transformações extras. O shell ksh93 também suporta ${ cmd;} como forma de substituição de comando que não gera um subshell.
  • ((…)) parênteses duplos cercam uma instrução aritmética , ou seja, um cálculo sobre inteiros, com uma sintaxe parecida com outras linguagens de programação. Essa sintaxe é usada principalmente para atribuições e em condicionais. Isto só existe em ksh / bash / zsh, não em sh simples.
    • A mesma sintaxe é usada nas expressões aritméticas $((…)) , que se expandem para o valor inteiro da expressão.
  • [ … ] colchetes individuais circundam expressões condicionais . As expressões condicionais são principalmente criadas em operadores como -n "$variable" para testar se uma variável estiver vazia e -e "$file" para testar se existe um arquivo. Observe que você precisa de um espaço ao redor de cada operador (por exemplo, [ "$x" = "$y" ] , não [ "$x"="$y" ] ) e um espaço ou um caractere como ; dentro e fora dos colchetes (por exemplo, [ -n "$foo" ] , não [-n "$foo"] ).
  • [[ … ]] colchetes duplos são uma forma alternativa de expressões condicionais em ksh / bash / zsh com alguns recursos adicionais, por exemplo, você pode escrever [[ -L $file && -f $file ]] para testar se um arquivo é um link simbólico para um arquivo regular, enquanto colchetes simples requerem% código%. Consulte Por que a expansão de parâmetro com espaços sem aspas funciona dentro de colchetes duplos [ [mas não colchetes únicos [? para mais sobre este tópico.

No shell, o comando every é um comando condicional: cada comando tem um status de retorno que é 0 indicando sucesso ou um inteiro entre 1 e 255 (e potencialmente mais em alguns shells) indicando falha . O comando [ -L "$file" ] && [ -f "$file" ] (ou [ … ] syntax form) é um comando específico que também pode ser escrito [[ … ]] e é bem-sucedido quando existe um arquivo, ou quando uma sequência não é vazia ou quando um número é menor que outro O formulário de sintaxe test … é bem-sucedido quando um número é diferente de zero. Aqui estão alguns exemplos de condicionais em um script de shell:

  • Teste se ((…)) contém a string myfile :

    if grep -q hello myfile; then …
    
  • Se hello for um diretório, mude para ele e faça as coisas:

    if cd mydir; then
      echo "Creating mydir/myfile"
      echo 'some content' >myfile
    else
      echo >&2 "Fatal error. This script requires mydir to exist."
    fi
    
  • Teste se existe um arquivo chamado mydir no diretório atual:

    if [ -e myfile ]; then …
    
  • O mesmo, mas também incluindo links simbólicos pendentes:

    if [ -e myfile ] || [ -L myfile ]; then …
    
  • Teste se o valor de myfile (que é considerado numérico) é pelo menos 2, portably:

    if [ "$x" -ge 2 ]; then …
    
  • Teste se o valor de x (que é considerado numérico) é de pelo menos 2, em bash / ksh / zsh:

    if ((x >= 2)); then …
    
por 28.08.2016 / 00:45
16

Da documentação do bash :

(list) list is executed in a subshell environment (see COMMAND EXECUTION ENVIRONMENT below). Variable assignments and builtin commands that affect the shell's environment do not remain in effect after the command completes. The return status is the exit status of list.

Em outras palavras, você garante que o que acontecer na 'lista' (como cd ) não tenha efeito fora do ( e ) . A única coisa que vai vazar é o código de saída do último comando ou com set -e o primeiro comando que gera um erro (além de alguns, como if , while , etc.)

((expression)) The expression is evaluated according to the rules described below under ARITHMETIC EVALUATION. If the value of the expression is non-zero, the return status is 0; otherwise the return status is 1. This is exactly equivalent to let "expression".

Esta é uma extensão bash que permite fazer matemática. Isso é um pouco semelhante ao uso de expr sem todas as limitações de expr (como ter espaços em todos os lugares, escapar * , etc.)

[[ expression ]] Return a status of 0 or 1 depending on the evaluation of the conditional expression expression. Expressions are composed of the primaries described below under CONDITIONAL EXPRESSIONS. Word splitting and pathname expansion are not performed on the words between the [[ and ]]; tilde expansion, parameter and variable expansion, arithmetic expansion, command substitution, process substitution, and quote removal are performed. Conditional operators such as -f must be unquoted to be recognized as primaries.

When used with [[, the < and > operators sort lexicographically using the current locale.

Isso oferece um teste avançado para comparar strings, números e arquivos um pouco como test , mas mais poderoso.

[ expr ] Return a status of 0 (true) or 1 (false) depending on the evaluation of the conditional expression expr. Each operator and oper and must be a separate argument. Expressions are composed of the primaries described above under CONDITIONAL EXPRESSIONS. test does not accept any options, nor does it accept and ignore an argument of -- as signifying the end of options.

[...]

Este chama test . Na verdade, nos velhos tempos, [ era um link simbólico para test . Funciona da mesma maneira e você tem as mesmas limitações. Como um binário sabe o nome com o qual foi iniciado, o programa de teste pode analisar parâmetros até encontrar um parâmetro ] . Diversão truques do Unix.

Observe que, no caso de bash , [ e test são funções internas (como mencionado em um comentário), ainda assim aplicam-se as mesmas limitações.

    
por 27.08.2016 / 21:06
12

Alguns exemplos:

Teste tradicional:

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

test e [ são comandos como qualquer outro, então a variável é dividida em palavras, a menos que esteja entre aspas.

Teste de novo estilo

[[ ... ]] é uma construção de shell especial (mais recente), que funciona de maneira um pouco diferente, a coisa mais óbvia é que ela não divide variáveis de palavras:

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

Algumas documentação sobre [ e [[ here .

Teste de aritmética:

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

Comandos "normais":

Todas as ações acima agem como os comandos normais e if pode receber qualquer comando:

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

Vários comandos:

Ou podemos usar vários comandos. Envolver um conjunto de comandos em ( ... ) os executa em subshell, criando uma cópia temporária do estado do shell (diretório de trabalho, variáveis). Se precisarmos executar algum programa temporariamente em outro diretório:

# 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 ...
    
por 27.08.2016 / 21:33
7

[ vs [[

Essa resposta cobrirá o [ vs [[ subconjunto da pergunta.

Algumas diferenças no Bash 4.3.11:

  • Extensão POSIX vs Bash:

  • comando regular vs magia

    • [ é apenas um comando normal com um nome estranho.

      ] é apenas um argumento de [ que impede que outros argumentos sejam usados.

      O Ubuntu 16.04 na verdade tem um executável para ele em /usr/bin/[ fornecido pelo coreutils, mas a versão integrada do bash tem precedência.

      Nada é alterado no modo como o Bash analisa o comando.

      Em particular, < é redirecionamento, && e || concatenam vários comandos, ( ) gera subshells, a menos que escape por \ , e a expansão de palavras ocorre normalmente.

    • [[ X ]] é uma única construção que faz com que X seja analisado magicamente. < , && , || e () são tratados especialmente e as regras de divisão de palavras são diferentes.

      Existem outras diferenças, como = e =~ .

      No Bashese: [ é um comando interno e [[ é uma palavra-chave: link

  • <

    • [[ a < b ]] : comparação lexicográfica
    • [ a \< b ] : o mesmo que acima. \ exigido ou então faz o redirecionamento como para qualquer outro comando. Extensão Bash.
    • Não consegui encontrar uma alternativa POSIX para isso, consulte: link
  • && e ||

    • [[ a = a && b = b ]] : verdadeiro, lógico e
    • [ a = a && b = b ] : erro de sintaxe, && analisado como um separador de comando AND cmd1 && cmd2
    • [ a = a -a b = b ] : equivalente, mas preterido por POSIX
    • [ a = a ] && [ b = b ] : recomendação POSIX
  • (

    • [[ (a = a || a = b) && a = b ]] : false
    • [ ( a = a ) ] : erro de sintaxe, () é interpretado como um subshell
    • [ \( a = a -o a = b \) -a a = b ] : equivalente, mas () foi preterido por POSIX
    • ([ a = a ] || [ a = b ]) && [ a = b ] recomendação POSIX
  • divisão de palavras

    • x='a b'; [[ $x = 'a b' ]] : true, aspas não são necessárias
    • x='a b'; [ $x = 'a b' ] : erro de sintaxe, expande para [ a b = 'a b' ]
    • x='a b'; [ "$x" = 'a b' ] : equivalente
  • =

    • [[ ab = a? ]] : true, porque faz correspondência de padrões ( * ? [ são mágicos) . Não glob expandir para arquivos no diretório atual.
    • [ ab = a? ] : a? glob expande. Então, pode ser verdadeiro ou falso, dependendo dos arquivos no diretório atual.
    • [ ab = a\? ] : false, não expansão glob
    • = e == são os mesmos em [ e [[ , mas == é uma extensão Bash.
    • printf 'ab' | grep -Eq 'a.' : equivalente a POSIX ERE
    • [[ ab =~ 'ab?' ]] : false, perde a magia com ''
    • [[ ab? =~ 'ab?' ]] : true
  • =~

    • [[ ab =~ ab? ]] : true, POSIX correspondência regular estendida , ? não é glob expandir
    • [ a =~ a ] : erro de sintaxe. Nenhum bash equivalente.
    • printf 'ab' | grep -Eq 'ab?' : equivalente a POSIX

Recomendação : use sempre [] .

Existem equivalentes POSIX para todas as construções [[ ]] que vi.

Se você usa [[ ]] you:

  • perder portabilidade
  • forçar o leitor a aprender as complexidades de outra extensão bash. [ é apenas um comando normal com um nome estranho, sem uma semântica especial.
por 30.11.2017 / 16:09
1

Comandos de agrupamento

Bash provides two ways to group a list of commands to be executed as a unit.

( list ) Colocar uma lista de comandos entre parênteses faz com que um ambiente subshell seja criado e cada um dos comandos na lista seja executado nessa sub-camada. Como a lista é executada em um subshell, as atribuições de variáveis não permanecem em vigor depois que a subshell é concluída.

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

{ list; } Colocar uma lista de comandos entre chaves faz com que a lista seja executada no contexto da shell atual . Nenhum subshell é criado. A lista seguinte de ponto e vírgula (ou nova linha) é necessária. Fonte

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

Construções Condicionais

Bracket único , ou seja, []
Para comparação ==, !=, <, e > e deve ser usado e para comparação numérica eq, ne,lt e gt devem ser usados.

Parênteses reforçados , ou seja, [[]]

Em todos os exemplos acima, usamos somente colchetes únicos para delimitar a expressão condicional, mas o bash permite colchetes duplos que servem como uma versão aprimorada da sintaxe de colchetes simples.

Para comparação, ==, !=, <, e > podem ser usados literalmente.

  • [ é um sinônimo para o comando de teste. Mesmo se estiver embutido no shell, ele cria um novo processo.
  • [[ é uma nova versão aprimorada, que é uma palavra-chave, não um programa.
  • [[ é entendido por Korn e Bash .

Fonte

    
por 13.01.2018 / 01:51

Tags