Eu quero combinar várias condições em uma declaração shell if 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 conforme o esperado. No entanto, me ocorre que não sei o que os parênteses estão fazendo ali. Eu quero usá-los apenas para agrupar, mas é realmente gerando um subshell? (Como posso saber?) Em caso afirmativo, existe uma maneira de agrupar as condições sem gerar um subshell?
bash
shell-script
Curinga
fonte
fonte
if ! [ -f file1 ] || ! [ -f file 2 ] || ! [ -f file3 ] ; then
mas gostaria de uma resposta mais geral.if [[ ! -f file1 ]] && [[ ! -f file2 ]]; then
if [ ! 1 -eq 2 ] && [ ! 2 -eq 3 ]; then echo yep; fi
e funciona. Eu sempre escrevo testes com aparelho duplo por uma questão de hábito. Além disso, para garantir que não sejabash
, testei ainda maisif /bin/test ! 1 -eq 2 && /bin/test ! 2 -eq 3 ; then echo yep; fi
e funciona dessa maneira também.[ ! -e file/. ] && [ -r file ]
irá soltar diretórios. negue como quiser. claro, é isso que-d
faz.Respostas:
Você precisa usar em
{ list;}
vez de(list)
:Ambos são comandos de agrupamento , mas
{ list;}
executam comandos no ambiente atual do shell.Observe que, o
;
in{ list;}
é necessário para delimitar a lista da}
palavra reversa, você também pode usar outro delimitador. O espaço (ou outro delimitador) depois{
também é necessário.fonte
awk
mais frequentemente do que nasbash
) é a necessidade de espaço em branco após a chave aberta. Você menciona "você também pode usar outro delimitador"; alguns exemplos?zsh
, você pode usar espaços em branco&
também é um separador, você pode usar{ echo 1;:&}
, várias linhas novas também podem ser usadas.they must be separated from list by whitespace or another shell metacharacter.
No bash são:metacharacter: | & ; ( ) < > space tab
. Para esta tarefa específica, acredito que algum& ; ) space tab
deles funcionará. ... .... Do zsh (como um comentário)each sublist is terminated by
&',
&!', or a newline.
Você pode usar inteiramente a
test
funcionalidade para alcançar o que deseja. Na página do manual detest
:Portanto, sua condição pode parecer com:
Para negar o uso de parênteses escapados:
fonte
Para portably negar uma condicional complexo no shell, você deve aplicar a lei de De Morgan e empurre a negação todo o caminho dentro das
[
chamadas ...... ou você deve usar
then :; else
...if ! command
não está disponível de forma portável e nem está[[
.Se você não precisar de portabilidade total, não escreva um script de shell . É mais provável que você encontre
/usr/bin/perl
um Unix selecionado aleatoriamente do que vocêbash
.fonte
!
é POSIX, que hoje em dia é portátil o suficiente. Mesmo os sistemas que ainda são fornecidos com o shell Bourne também possuem um sh POSIX em outro lugar do sistema de arquivos que você pode usar para interpretar sua sintaxe padrão./bin/sh
. Os scripts do Autoconf fazem o que você sugere, mas acho que todos sabemos quão pouco é esse endosso.outros notaram o agrupamento de
{
comandos compostos;}
, mas se você estiver executando testes idênticos em um conjunto, poderá usar um tipo diferente:... como é demonstrado em outro lugar
{ :;}
, não há dificuldade em aninhar comandos compostos ...Observe que o acima (normalmente) testa arquivos regulares . Se você estiver procurando apenas arquivos legíveis e existentes que não sejam diretórios:
Se você não se importa se são diretórios ou não:
... funciona para qualquer arquivo legível e acessível , mas provavelmente ficará travado por quinze sem gravadores.
fonte
Esta não é uma resposta completa à sua pergunta principal, mas notei que você mencionou o teste composto (arquivo legível) em um comentário; por exemplo,
Você pode consolidar isso um pouco, definindo uma função shell; por exemplo,
Adicione o tratamento de erros a (por exemplo,
[ $# = 1 ]
) a gosto. A primeiraif
declaração, acima, agora pode ser condensada parae você pode encurtar ainda mais, encurtando o nome da função. Da mesma forma, você pode definir
not_readable_file()
(ounrf
abreviar) e incluir a negação na função.fonte
readable_files() { [ $# -eq 0 ] && return 1 ; for file in "$@" ; do [ -f "$file" ] && [ -r "$file" ] || return 1 ; done ; }
(Disclaimer: Eu testei este código e ele funciona, mas ele não era um one-liner eu adicionei ponto e vírgula para postar aqui, mas não testar sua colocação..)if readable_files file1 file2 file3
não sabe se a função está executando AND ou OR - embora (a) acho que seja bastante intuitivo, (b) se você não estiver distribuindo o script, é bom o suficiente se você o entender, e (c) desde que sugeri abreviar o nome da função para a obscuridade, não estou em posição de falar sobre legibilidade. ... (continua)for file in "$@" ; do
comfor file do
- o padrão éin "$@"
, e (um pouco contra-intuitiva) o;
não é apenas desnecessário, mas realmente desanimado.Você também pode negar os testes de chaves, para reutilizar seu código original:
fonte
||
vez de&&
||
executaria o predicado se algum dos arquivos não estiver presente, não se e somente se todos os arquivos não estiverem presentes. NOT (A e B e C) é o mesmo que (NÃO A) E (NÃO B) E (NÃO C); veja a pergunta original.||
vez de&&
. Veja meu primeiro comentário abaixo da minha pergunta em si.not (a and b)
é o mesmo que(not a) or (not b)
. Veja en.wikipedia.org/wiki/De_Morgan%27s_lawsSim, 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.
Exemplo de saída, demonstrando que os parênteses geram um subshell e as chaves não:
fonte
()
faz subshell..perhaps desova você significou{}
....echo $$ && ( echo $$ )
para comparar os PIDs e eles eram os mesmos, mas, pouco antes de enviar o comentário, dizendo que você estava errado, tentei outra coisa.echo $BASHPID && ( echo $BASHPID )
dá PID's diferentesecho $BASHPID && { echo $BASHPID; }
dê o mesmo PID que você sugeriu que seria o caso. Obrigado pela educação$BASHPID
, essa é a outra coisa que estava faltando.