bash se não várias condições sem subshell?

15

Eu quero combinar várias condições em uma shell if statement e negar a combinação. Eu tenho o seguinte código de trabalho para uma combinação simples de condições:

if [ -f file1 ] && [ -f file2 ] && [ -f file3 ] ; then
  # do stuff with the files
fi

Isso funciona bem. Se eu quiser negar, posso usar o seguinte código de trabalho:

if ! ( [ -f file1 ] && [ -f file2 ] && [ -f file3 ] ) ; then
  echo "Error: You done goofed."
  exit 1
fi
# do stuff with the files

Isso também funciona como esperado. No entanto, ocorre-me que não sei o que os parênteses estão realmente fazendo lá. Eu quero usá-los apenas para agrupamento, mas ele está realmente gerando um subshell? (Como posso saber?) Em caso afirmativo, existe uma maneira de agrupar as condições sem gerar uma subcamada?

    
por Wildcard 03.12.2015 / 19:30

7 respostas

24

Você precisa usar { list;} em vez de (list) :

if ! { [ -f file1 ] && [ -f file2 ] && [ -f file3 ]; }; then
  : do something
fi

Ambos são Comandos de agrupamento , mas { list;} executa comandos no momento ambiente de shell.

Observe que o ; em { list;} é necessário para delimitar a lista de } palavra reversa, você também pode usar outro delimitador. O espaço (ou outro delimitador) após { também é necessário.

    
por 03.12.2015 / 19:36
8

Você pode usar totalmente a funcionalidade test para obter o que deseja. Na página man de test :

 ! expression  True if expression is false.
 expression1 -a expression2
               True if both expression1 and expression2 are true.
 expression1 -o expression2
               True if either expression1 or expression2 are true.
 (expression)  True if expression is true.

Assim, sua condição pode parecer:

if [ -f file1 -a -f file2 -a -f file3 ] ; then
    # do stuff with the files
fi

Para negar use parênteses com escape:

if [ ! \( -f file1 -a -f file2 -a -f file3 \) ] ; then
    echo "Error: You done goofed."
    exit 1
fi
# do stuff with the files
    
por 04.12.2015 / 02:48
5

Para portably negar uma condicional complexa no shell, você deve aplicar lei de De Morgan e empurre a negação para baixo dentro das chamadas [ ...

if [ ! -f file1 ] || [ ! -f file2 ] || [ ! -f file3 ]
then
    # do stuff
fi

... ou você deve usar then :; else ...

if [ -f file1 ] && [ -f file2 ] && [ -f file3 ]
then :
else
  # do stuff
fi

if ! command não está disponível portualmente e nem é [[ .

Se você não precisa de portabilidade total, não escreva um script de shell . Você está, na verdade, mais em encontrar /usr/bin/perl em um Unix selecionado aleatoriamente do que você é bash .

    
por 03.12.2015 / 20:27
5

outras pessoas notaram o { compound command ;} grouping, mas se você estiver executando testes idênticos em um set , poderá usar um tipo diferente:

if  ! for f in file1 file2 file3
      do  [ -f "$f" ] || ! break
      done
then  : do stuff
fi

... como é demonstrado em outros lugares com { :;} , não há nenhuma dificuldade envolvida em aninhar comandos compostos ...

Observe que os testes (normalmente) acima para arquivos regulares . Se você está procurando apenas arquivos existentes , legíveis que não são diretórios:

if  ! for f in file1 file2 file3
      do  [ ! -d "$f" ] && 
          [   -r "$f" ] || ! break
      done
then  : do stuff
fi

Se você não se importa se eles são diretórios ou não:

if  ! command <file1 <file2 <file3
then  : do stuff
fi

... funciona para qualquer arquivo legível , acessível , mas provavelmente será interrompido por autores de fifos w / out.

    
por 03.12.2015 / 21:50
2

Esta não é uma resposta completa à sua pergunta principal, mas notei que você menciona teste composto (arquivo legível) em um comentário; por exemplo,

if [ -f file1 ] && [ -r file1 ] && [ -f file2 ] && [ -r file2 ] && [ -f file3 ] && [ -r file3 ]

Você pode consolidar isso um pouco definindo uma função de shell; por exemplo,

readable_file()
{
    [ -f "$1" ]  &&  [ -r "$1" ]
}

Adicione tratamento de erros a (por exemplo, [ $# = 1 ] ) para provar. A primeira declaração if , acima, agora pode ser condensada para

if readable_file file1  &&  readable_file file2  &&  readable_file file3

e você pode encurtá-lo ainda mais encurtando o nome da função. Da mesma forma, você pode definir not_readable_file() (ou nrf para abreviar) e incluir a negação na função.

    
por 04.12.2015 / 01:22
0

Você também pode negar os testes internos, para reutilizar seu código original:

if [[ ! -f file1 ]] || [[ ! -f file2 ]] || [[ ! -f file3 ]] ; then
  # do stuff with the files
fi
    
por 03.12.2015 / 19:41
-1

I want to use them just for grouping, but is it actually spawning a subshell? (How can I tell?)

Sim, os comandos dentro do (...) são executados em um subshell.

Para testar se você está em um subshell, você pode comparar o PID do seu shell atual com o PID do shell interativo.

echo $BASHPID; (echo $BASHPID); 

Exemplo de saída, demonstrando que os parênteses geram um subshell e chaves não:

$ echo $BASHPID; (echo $BASHPID); { echo $BASHPID;}
50827
50843
50827
$ 
    
por 03.12.2015 / 19:34