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

)

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

[[ , [ , ( , (( 

Eu vi pessoas os usarem em declarações if como este:

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

Comentários

  • Relacionados: usando colchetes simples ou duplo – bash
  • Parênteses e colchetes não são ‘ tão fáceis de pesquisar na documentação e ‘ é tudo o que você tem se não ‘ não souber os nomes desses recursos.

Resposta

Em shells tipo Bourne, uma instrução if normalmente se parece com

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

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

  1. if [ condition ]

    [ é outro nome para o comando test tradicional. [ / test é um utilitário POSIX padrão. Todos os shells POSIX o têm embutido (embora isso “não seja exigido pelo POSIX²). O comando test define um código de saída e a instrução if age de acordo. Os 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 , zsh , yash , busybox sh também suportam. Esta [[ ... ]] construção também define um código de saída e o if atua de acordo. Entre seus recursos estendidos, pode testar se uma string corresponde a um padrão curinga (não em busybox sh ).

  3. if ((condition))

    Outra extensão ksh que bash e zsh também suportam. Isso executa a aritmética. Como resultado da aritmética, um código de saída é definido e a instrução if age acco rdingly. Ele retorna um código de saída 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átil.

  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.

    Um motivo típico para usar um subshell como este é limitar os efeitos colaterais de command if command atribuições de variáveis necessárias ou outras alterações no ambiente do shell. Essas alterações não permanecem após a conclusão do subshell.

  5. if command

    comando é executado e a instrução if atua de acordo com seu código de saída.


¹ embora não seja realmente um comando, mas uma construção de shell especial com sua própria sintaxe separada da do comando normal, e variando significativamente entre as implementações de shell

² POSIX requer que haja um test e [ utilitários no sistema, entretanto, embora no caso de [, várias distribuições Linux sejam conhecidas por serem m emitindo-o.

Comentários

  • Obrigado por incluir a 5ª opção. Essa ‘ é a chave para entender como isso realmente funciona e é surpreendentemente subutilizada.
  • Observe que [ é na verdade um binário, não um comando ou símbolo interno. Geralmente reside em /bin.
  • @JulienR. na verdade, [ é integrado, assim como test. Existem versões binárias disponíveis por razões de compatibilidade. Verifique help [ e help test.
  • Vale a pena observar que enquanto ((não é POSIX, $(( ou seja, a expansão aritmética é e ‘ é fácil confundi-los. Freqüentemente, uma solução alternativa é usar algo como [ $((2+2)) -eq 4 ] para fazer uso da aritmética em declarações condicionais
  • Gostaria de poder votar a favor desta resposta mais de uma vez. Explicação perfeita.

Resposta

  • (…) parênteses indicam um subshell . O que está dentro deles não é uma expressão como em muitas outras línguas. É uma lista de comandos (como fora dos parênteses). 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 : há um comando entre parênteses e a saída do comando é usado como parte da linha de comando (após expansões extras, a menos que a substituição seja entre aspas duplas, mas isso “s outra história ).
  • { … } colchetes são como parênteses porque 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 os colchetes sendo palavras-chave precisam ser delimitados e encontrado na posição de comando (daí o espaço após { e o ; antes de }), enquanto parênteses não. Isso é apenas uma peculiaridade de sintaxe.)
    • Com um cifrão à esquerda, ${VAR} é um expansão do parâmetro , expandindo para o valor de uma variável, com possíveis transformações extras. O shell ksh93 também oferece suporte a ${ 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 em números inteiros, com uma sintaxe semelhante a outras linguagens de programação. Essa sintaxe é usada principalmente para atribuições e em condicionais. Isso só existe em ksh / bash / zsh, não em sh puro.
    • A mesma sintaxe é usada em expressões aritméticas $((…)), que se expande para o valor inteiro da expressão.
  • [ … ] colchetes simples circundam expressões condicionais . Expressões condicionais são construídas principalmente em operadores , como -n "$variable" para testar se uma variável está 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 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 os colchetes simples requerem [ -L "$file" ] && [ -f "$file" ]. Consulte Por que a expansão do parâmetro com espaços sem aspas funciona entre colchetes duplos [[mas não colchetes simples [? para obter mais informações sobre este tópico.

No shell, cada comando é 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 fracasso. O [ … ] comando (ou [[ … ]] forma de sintaxe) é um comando específico que também pode ser escrito test … e é bem-sucedido quando existe um arquivo, ou quando uma string não está vazia, ou quando um número é menor que outro, etc. A forma de sintaxe ((…)) é bem-sucedida quando um número é diferente de zero .Aqui estão alguns exemplos de condicionais em um script de shell:

  • Teste se myfile contém a string hello:

    if grep -q hello myfile; then … 
  • Se mydir for um diretório, mude para e faça outras 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 há um arquivo chamado myfile 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 x (que se presume ser numérico) é pelo menos 2, portavelmente:

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

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

Comentários

  • Observe que o colchete único é compatível com -a em vez de &&, para que se possa escrever: [ -L $file -a -f $file ], que é o mesmo número de caracteres entre colchetes sem o [ e ]
  • @AlexisWilke Os operadores -a e -o são problemático porque eles podem levar a análises incorretas se alguns dos operandos envolvidos se parecem com operadores. É ‘ por isso que não ‘ não os menciono: eles têm vantagem zero e não ‘ t sempre funciona. E nunca escreva expansões de variáveis não citadas sem um bom motivo: [[ -L $file -a -f $file ]] está bem, mas com colchetes simples você precisa de [ -L "$file" -a -f "$file" ] (que está ok, por exemplo, se $file sempre começa com / ou ./).
  • Observe que ele ‘ s [[ -L $file && -f $file ]] (não -a com o [[...]] variante).

Resposta

[ vs [[

Esta 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 o último argumento de [.

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

    Nada é alterado na maneira como o Bash analisa o comando.

    Em particular, < é redirecionamento, && e || concatena comandos múltiplos, ( ) gera subshells, a menos que escapado por \, e a expansão de palavras acontece normalmente.

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

      Existem também outras diferenças como = e =~ .

    Em bashês: [ é um comando integrado e [[ é uma palavra-chave: https://askubuntu.com/questions/445749/whats-the-difference-between-shell-builtin-and-shell-keyword

  • <

  • && 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 ] && [ b = b ]: POSIX equivalente confiável
    • [ a = a -a b = b ]: quase equivalente, mas obsoleto pelo POSIX porque é insano e falha para alguns valores de a ou b como ! ou ( que seriam interpretadas como operações lógicas
  • (

    • [[ (a = a || a = b) && a = b ]]: falso. Sem ( ), seria verdadeiro porque [[ && ]] tem precedência maior do que [[ || ]]
    • [ ( a = a ) ]: erro de sintaxe, () é interpretado como um subshell
    • [ \( a = a -o a = b \) -a a = b ]: equivalente, mas (), -a e -o estão obsoletos por POSIX. Sem \( \) seria verdadeiro porque -a tem maior precedência do que -o
    • { [ a = a ] || [ a = b ]; } && [ a = b ] equivalente POSIX não obsoleto. Neste caso específico, entretanto, poderíamos ter escrito apenas: [ a = a ] || [ a = b ] && [ a = b ] porque || e && os operadores de shell têm precedência igual, ao contrário de [[ || ]] e [[ && ]] e -o, -a e [
  • divisão de palavras e geração de nome de arquivo nas expansões (divisão + glob )

    • x="a b"; [[ $x = "a b" ]]: verdadeiro, aspas não são necessárias
    • x="a b"; [ $x = "a b" ]: sintaxe erro, expande para [ a b = "a b" ]
    • x="*"; [ $x = "a b" ]: erro de sintaxe se houver mais de um arquivo no diretório atual .
    • x="a b"; [ "$x" = "a b" ]: POSIX equivalente
  • =

    • [[ ab = a? ]]: verdadeiro, porque faz correspondência de padrões ( * ? [ são mágicos). Não se expande para f arquivos no diretório atual.
    • [ ab = a? ]: a? glob se expande. Portanto, pode ser verdadeiro ou falso dependendo dos arquivos no diretório atual.
    • [ ab = a\? ]: falso, não expansão glob
    • = e == são iguais em [ e [[, mas == é uma extensão Bash.
    • case ab in (a?) echo match; esac: equivalente POSIX
    • [[ ab =~ "ab?" ]]: false, perde magia com "" no Bash 3.2 e superior e a compatibilidade fornecida com o bash 3.1 não está habilitada (como com BASH_COMPAT=3.1)
    • [[ ab? =~ "ab?" ]]: verdadeiro
  • =~

    • [[ ab =~ ab? ]]: true, POSIX estendido correspondência de expressão regular , ? não se expande global
    • [ a =~ a ]: erro de sintaxe. Sem equivalente bash.
    • printf "ab\n" | grep -Eq "ab?": equivalente POSIX (apenas dados de uma linha)
    • awk "BEGIN{exit !(ARGV[1] ~ ARGV[2])}" ab "ab?": POSIX equivalente.

Recomendação: sempre use []

Existem equivalentes POSIX para cada [[ ]] construção que eu “vi.

Se você usar [[ ]] você:

  • perde portabilidade
  • força o leitor a aprender as complexidades de outra extensão bash. [ é apenas um comando regular com um nome estranho, sem semântica especial envolvida.

Graças a Stéphane Chazelas para correções e acréscimos importantes.

Comentários

  • @St é phaneChazelas obrigado pela informação! Eu ‘ adicionei expr à resposta. O termo ” Bash ex tension ” não significa que o Bash foi o primeiro shell a adicionar alguma sintaxe, aprender POSIX sh vs Bash já é o suficiente para me deixar louco.
  • Veja man test se você tentou man [ e se perdeu. Isso explicará a variante POSIX.
  • Correção: ] é um argumento para o comando [, mas não ‘ t evita que outros argumentos sejam usados. ] deve ser o último argumento para [, mas também pode ocorrer como parte da expressão de teste. Por exemplo, if [ "$foo" = ] ]; then testará se a variável foo está definida como “] ” (assim como if [ ] = "$foo" ]; then).
  • @GordonDavisson obrigado, não ‘ t sei disso, consertado.
  • @ tgm1024 – Monica foi maltratada sim, essa também é uma consideração válida.

Resposta

Da documentação do bash :

(list) lista é executada em um ambiente de subshell (consulte AMBIENTE DE EXECUÇÃO DE COMANDO abaixo). Atribuições de variáveis e comandos integrados que afetam o ambiente do shell não permanecem em vigor após a conclusão do comando. O status de retorno é o status de saída da lista.

Em outras palavras, você deve certificar-se de que tudo o que acontece em “list” (como cd) não tem efeito fora de ( e ). A única coisa que vazará é o código de saída do último comando ou com set -e o primeiro comando que gera um erro (outro do que alguns, como if, while, etc.)

((expression)) A expressão é avaliada de acordo com as regras descritas abaixo em AVALIAÇÃO ARITMÉTICA. Se o valor da expressão for diferente de zero, o status de retorno é 0; caso contrário, o status de retorno é 1. Isso é exatamente equivalente a let ” expressão “.

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

[[ expression ]] Retorne um status de 0 ou 1 dependendo do avaliação da expressão da expressão condicional. As expressões são compostas das primárias descritas abaixo em EXPRESSÕES CONDICIONAIS. A divisão de palavras e a expansão do nome do caminho não são realizadas nas palavras entre [[e]]; Expansão til, expansão de parâmetro e variável, expansão aritmética, substituição de comando, substituição de processo e remoção de cotação são executadas. Operadores condicionais como -f devem estar sem aspas para serem reconhecidos como primários.

Quando usado com [[, o < e > os operadores classificam lexicograficamente usando o local atual.

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

[ expr ] Retorna um status de 0 (verdadeiro) ou 1 (falso) dependendo da avaliação da expressão condicional expr. Cada operador e oper e deve ser um argumento separado. As expressões são compostas das primárias descritas acima em EXPRESSÕES CONDICIONAIS. teste não aceita nenhuma opção, nem aceita e ignora um argumento de – como significando o fim das opções.

[…]

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. Visto que um binário sabe o nome com o qual foi iniciado, o programa de teste sabe quando foi iniciado como [ e pode ignorar seu último parâmetro, que deve ser ]. Truques divertidos do Unix.

Observe que no caso de bash, [ e test são funções integradas (conforme mencionado em um comentário), mas praticamente as mesmas limitações se aplicam.

Comentários

  • Embora test e [ são, obviamente, comandos embutidos no Bash, mas ‘ é provável que um binário externo também existe.
  • O binário externo para [ não é um link simbólico para test na maioria dos sistemas modernos .
  • De alguma forma, acho divertido que eles se preocupem em criar dois binários separados, que têm exatamente o que precisam, em vez de apenas combiná-los e adicionar algumas condicionais. Embora na verdade strings /usr/bin/test mostre que também tem o texto de ajuda, não ‘ não sei o que dizer.
  • @ Random832 Eu entendi seu ponto sobre a lógica do GNU para evitar comportamento arg0 inesperado, mas sobre os requisitos POSIX, eu não ‘ não seria tão afirmativo. Embora o comando test seja obviamente necessário para existir como um comando baseado em arquivo independente pelo padrão, nada nele afirma que sua [ variante precisa ser implementado dessa forma também. Por exemplo, o Solaris 11 não ‘ não fornece nenhum [ executável, mas é totalmente compatível com os padrões POSIX
  • (saída 1) tem um efeito fora dos parênteses.

Resposta

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

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

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

Alguns documentação em [ e [[ aqui .

Teste aritmético:

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

“Normal “comandos:

Todos os acima agem como comandos normais e if pode receber qualquer comando:

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

Comandos múltiplos:

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

Resposta

Comandos de agrupamento

O Bash oferece duas maneiras de agrupar uma lista de comandos a serem executados como uma unidade.

( list ) Colocar uma lista de comandos entre parênteses faz com que um ambiente de subshell seja criado e cada um dos comandos na lista seja executado nesse subshell. Como a lista é executado em um subshell, as atribuições de variáveis não permanecem em vigor após a conclusão do subshell.

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

{ list; } Colocando um lista de comandos entre chaves faz com que a lista seja executada no contexto atual do shell . Nenhum subshell é criado. O ponto e vírgula (ou nova linha) a seguir lista é obrigatório. 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

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

Colchetes aprimorados ie [[]]

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

Para comparação, ==, !=, <, e > pode usar literalmente.

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

Fonte

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *