desenv-web-rp.com

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

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

[[ , [ , ( , ((

Já vi pessoas usá-las em declarações if como esta:

if [[condition]]

if [condition]

if ((condition))

if (condition)
298
RetroCode

Uma instruçã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 é executada. commands1 Pode ser simples ou complexo. Por exemplo, pode 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 Shell test tradicional. Está disponível em todos os shells POSIX. O comando test define um código de saída e a instrução if atua de acordo. Testes típicos são se existe um arquivo ou se um número é igual a outro.

  2. if [[ condition ]]

    Esta é uma nova variação atualizada de test de ksh que bash e zsh também suportam. Este comando test também define um código de saída e a instrução if atua 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 executa aritmética. Como resultado da aritmética, um código de saída é definido e a instrução if atua de acordo. Retorna um código de saída igual a 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 atua de acordo.

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

  5. if command

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

316
John1024
  • (…) Parênteses indicam um subshell . O que há 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., realizado entre parênteses, não tem efeito fora dos parênteses.
    • Com um cifrão à esquerda, $(…) é um substituição de comando : existe um comando entre parênteses e a saída do comando é usada como parte da linha de comando (após extra expansões, a menos que a substituição seja entre aspas duplas, mas isso é outra história ).
  • As chaves { … } São parênteses, pois agrupam comandos, mas apenas influenciam 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 encontrado na posição de comando (daí o espaço após { e o ; antes de }), enquanto os parênteses não. Isso é apenas uma peculiaridade da sintaxe.)
    • Com um sinal de dólar à 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 suporta ${ cmd;} Como forma de substituição de comando que não gera um subshell.
  • ((…)) Parênteses duplos cercam uma instrução aritmética , isto é, uma computação 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 existe apenas 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 simples cercam 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"]).
  • Os colchetes duplos [[ … ]] São uma forma alternativa de expressões condicionais no 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 arquivo, enquanto colchetes simples requerem [ -L "$file" ] && [ -f "$file" ]. Consulte Por que a expansão de parâmetros com espaços sem aspas funciona entre colchetes [[mas não entre colchetes [? para obter mais informações sobre este tópico.

No Shell, todo comando é um comando condicional: todo comando tem um status de retorno que é 0, indicando sucesso ou um número inteiro entre 1 e 255 (e potencialmente mais em algumas conchas) indicando falha. O comando [ … ] (Ou a forma da sintaxe [[ … ]]) É um comando específico que também pode ser escrito test … E é bem-sucedido quando um arquivo existe ou quando uma sequência não está vazia, ou quando um número é menor que outro, etc. O formato da sintaxe ((…)) é bem-sucedido quando um número é diferente de zero. Aqui estão alguns exemplos de condicionais em um script do Shell:

  • Teste se myfile contém a sequência hello:

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

    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 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 é assumido como numérico) é pelo menos 2, de forma portável:

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

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

Do documentação do bash :

(list) list é executada em um ambiente subshell (consulte AMBIENTE DE EXECUÇÃO DE COMANDO abaixo). As atribuições variáveis ​​e os comandos internos 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, verifique se o que quer que aconteça na 'lista' (como um cd) não tem efeito fora do ( 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 (além de 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 será 0; caso contrário, o status de retorno é 1. Isso é exatamente equivalente a deixar "expressão".

Esta é uma extensão do bash, permitindo que você faça contas. 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 ]] Retorne um status de 0 ou 1, dependendo da 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 executadas nas palavras entre os [[e]]; expansão de 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. Operadores condicionais como -f devem estar sem aspas para serem reconhecidos como primários.

Quando usados ​​com [[ os operadores <e> classificam lexicograficamente usando o código do idioma atual.

Isso oferece um teste avançado para comparar seqüências de caracteres, números e arquivos um pouco como o test oferece, mas mais poderoso.

[ expr ] Retorne 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. test 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. 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 ]. Truques divertidos do Unix.

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

20
Alexis Wilke

_[_ vs _[[_

Esta resposta cobrirá o subconjunto _[_ vs _[[_ da pergunta.

Algumas diferenças no Bash 4.3.11:

  • Extensão POSIX vs Bash:

  • comando regular vs magia

    • _[_ é apenas um comando regular com um nome estranho.

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

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

      Nada é alterado na maneira como Bash analisa o comando.

      Em particular, _<_ é redirecionamento, _&&_ e _||_ concatenam vários comandos, _( )_ gera subshells, a menos que seja escapado por _\_, e a expansão do Word acontece como de costume.

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

      Também existem outras diferenças, como _=_ e _=~_.

      No Bashese: _[_ é um comando interno e _[[_ é uma palavra-chave: https://askubuntu.com/questions/445749/whats-the-difference-between-Shell -builtin-e-Shell-palavra-chave

  • _<_

  • _&&_ e _||_

    • _[[ a = a && b = b ]]_: verdadeiro, lógico e
    • _[ a = a && b = b ]_: erro de sintaxe, _&&_ analisado como um separador de comandos AND _cmd1 && cmd2_
    • _[ a = a -a b = b ]_: equivalente, mas reprovado pelo POSIX³
    • _[ a = a ] && [ b = b ]_: POSIX e equivalente confiável
  • _(_

    • [[ (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 _()_ está obsoleto pelo POSIX
    • _{ [ a = a ] || [ a = b ]; } && [ a = b ]_ POSIX equivalente5
  • Divisão de palavras e geração de nome de arquivo após expansões (divisão + glob)

    • _x='a b'; [[ $x = 'a b' ]]_: true, aspas não necessárias
    • _x='a b'; [ $x = 'a b' ]_: erro de sintaxe, 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' ]_: equivalente ao POSIX
  • _=_

    • _[[ ab = a? ]]_: true, porque sim correspondência de padrão (_* ? [_ são mágicos). Não é expandido para arquivos no diretório atual.
    • _[ ab = a? ]_: _a?_ glob expande. Portanto, pode ser verdadeiro ou falso, dependendo dos arquivos no diretório atual.
    • _[ ab = a\? ]_: expansão falsa, não global
    • _=_ e _==_ são os mesmos em _[_ e _[[_, mas _==_ é uma extensão do Bash.
    • case ab in (a?) echo match; esac: equivalente a POSIX
    • _[[ ab =~ 'ab?' ]]_: false4, perde mágica com _''_
    • _[[ ab? =~ 'ab?' ]]_: verdadeiro
  • _=~_

    • _[[ ab =~ ab? ]]_: true, POSIX expressão regular estendida corresponde, _?_ não é expandido
    • _[ a =~ a ]_: erro de sintaxe. Nenhum equivalente do bash.
    • _printf 'ab\n' | grep -Eq 'ab?'_: equivalente ao POSIX (apenas dados de linha única)
    • awk 'BEGIN{exit !(ARGV[1] ~ ARGV[2])}' ab 'ab?': equivalente a POSIX.

Recomendação : use sempre _[]_.

Existem equivalentes POSIX para cada construção _[[ ]]_ que eu já vi.

Se você usar _[[ ]]_, você:

  • perder portabilidade
  • forçar o leitor a aprender os meandros de outra extensão do bash. _[_ é apenas um comando regular com um nome estranho, sem semântica especial envolvida.

¹ Inspirado no equivalente _[[...]]_ no Korn Shell

² mas falha para alguns valores de a ou b (como _+_ ou index) e faz comparação numérica se a e b parecem números inteiros decimais. _expr "x$a" '<' "x$b"_ funciona em torno de ambos.

³ e também falha em alguns valores de a ou b como _!_ ou _(_.

4 no bash 3.2 e acima e a compatibilidade fornecida com o bash 3.1 não está ativada (como em _BASH_COMPAT=3.1_)

5 embora o agrupamento (aqui com o grupo de comandos _{...;}_ em vez de _(...)_ que executaria um subshell desnecessário) não seja necessário, pois os operadores _||_ e _&&_ Shell (em oposição a os operadores _||_ e _&&_ _[[...]]_ ou os operadores _-o_/_-a_ _[_) têm precedência igual. Então _[ a = a ] || [ a = b ] && [ a = b ]_ seria equivalente.

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, portanto a variável é dividida em palavras, a menos que esteja entre aspas.

Teste de novo estilo

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

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

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

Teste aritmético:

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

Comandos "normais":

Todos os itens acima agem como comandos normais e if podem assumir qualquer comando:

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

Vários comandos:

Ou podemos usar vários comandos. Agrupando um conjunto de comandos em ( ... ) executa-os em subshell, criando uma temporária cópia do estado do Shell (diretório ativo, 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 ...
13
ilkkachu

Comandos de agrupamento

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

( list ) Colocar uma lista de comandos entre parênteses causa a criação de um ambiente de subshell, e cada um dos comandos na lista é executado nesse subshell. Como a lista é executada 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; } Colocar uma lista de comandos entre chaves faz com que a lista seja executada no contexto atual do Shell atual . Nenhum subshell é criado. É necessário o ponto e vírgula (ou nova linha) a seguir. Origem

${} 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 ou seja, []
Para comparação ==, !=, <, e > e deve ser usado e para comparação numérica eq, ne,lt e gt devem ser usados.

Suportes aprimorados ou seja, [[]]

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

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

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

Origem

1
Premraj