AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / unix / Perguntas / 726261
Accepted
Ton van den Heuvel
Ton van den Heuvel
Asked: 2022-11-26 03:57:37 +0800 CST2022-11-26 03:57:37 +0800 CST 2022-11-26 03:57:37 +0800 CST

Resultado inesperado para avaliação de lógica ou em POSIX sh condicional

  • 772

Suponha que temos uma instrução if da seguinte forma:

if [ $(false) ]; then
    echo "?"
fi

Então "?" não é impresso (a condição é falsa). No entanto, no caso a seguir, "?!" é impresso, por quê?

if [ $(false) -o $(false) ]; then
    echo "?!"
fi
shell
  • 2 2 respostas
  • 840 Views

2 respostas

  • Voted
  1. Best Answer
    Stephen Kitt
    2022-11-26T04:03:09+08:002022-11-26T04:03:09+08:00

    $(false)não for avaliado como falso, ele produzirá uma string vazia. Porque não é citado,

    if [ $(false) ]; then
    

    avalia para

    if [ ]; then
    

    que é falso, porque [com uma expressão vazia é falso.

    if [ $(false) -o $(false) ]; then
    

    avalia para

    if [ -o ]; then
    

    Isso não usa o -ooperador, ele avalia -ocomo uma expressão com uma única string; [com tal expressão é verdadeiro, então a thenparte da ifinstrução é executada.

    Consulte a especificação POSIX paratest , em particular:

    O algoritmo para determinar a precedência dos operadores e o valor de retorno que deve ser gerado é baseado no número de argumentos apresentados para testar. (No entanto, ao usar o formulário "[...]", o argumento final <colchete direito> não deve ser contado neste algoritmo.)

    Na lista a seguir, $1, $2, $3 e $4 representam os argumentos apresentados para testar:

    0 argumentos:
    Saída falsa (1).
    1 argumento:
    Sai verdadeiro (0) se $1 não for nulo; caso contrário, saia falso.

    testsó considera operadores se forem dados pelo menos dois argumentos.

    Se você quiser usar o status de saída de um comando como condição, não o coloque em uma substituição de comando ou em [ ]:

    if false; then
    

    e

    if false || false; then
    

    Observe também que os operadores test's -ae -osão obsoletos e não confiáveis; você deve usar o shell &&e ||os operadores em vez disso, por exemplo

    if [ "$a" = b ] || [ "$a" = c ]; then
    
    • 31
  2. Stéphane Chazelas
    2022-11-26T06:44:34+08:002022-11-26T06:44:34+08:00

    Não vou repetir os pontos levantados nas boas respostas de @StephenKitt e @ilkkachu aqui, apenas concentre-se no que significa escrever if [ $(cmd) ]; then....

    Primeiramente observe que não[ é um operador POSIXsh , é um utilitário separado também conhecido como , aqui utilizado testna ifparte de uma instrução if//// na sintaxe da linguagem .thenelifelsefish

    Sua função é avaliar seus argumentos como uma expressão condicional. Por exemplo, se ele receber [, a, =, be ]como argumentos ou test, a, =e b, ele retornará um status de saída falso/falha, pois o interpretou como "'a' é a mesma string que 'b'?" expressão condicional.

    [é freqüentemente usado como o único comando na ifparte de uma instrução if// thenou elif// ou na instrução // ou // , mas não precisa ser nem essas instruções precisam invocar elsenem um único comando.fiwhiledodoneuntildodone[

    Fazer [ $(cmd) ]significa chamar o [comando com [, o resultado da $(cmd)expansão e ]como argumentos separados. Em seguida, [interpreta esses argumentos como uma expressão condicional e retorna verdadeiro ou falso dependendo do resultado (também falso se não conseguir entender a expressão).

    Em POSIX sh, $(cmd)expande para a saída padrão de cmddespojado de todos os caracteres de nova linha à direita e, como não é citado e no contexto da lista, sujeito à divisão IFS e, em seguida, globbing (com o comportamento não especificado se houver caracteres NUL nessa saída) .

    Então if [ $(cmd) ]ou test $(cmd)realmente não faz sentido.

    É algo como fazer a pergunta: "a saída de cmd, uma vez dividida+globbed, constitui uma expressão condicional válida que resulta em true?"

    Por exemplo, se cmdas saídas a,*e $IFScontiverem ,e o diretório de trabalho atual contiver dois arquivos, um chamado =, outro chamado b, [ $(cmd) ]chamará [com [, a, =, b, ]como argumentos. Felizmente, essa é uma expressão condicional válida, mas que retorna false como anão é b.

    $ cd "$(mktemp -d)"
    $ touch = b
    $ cmd() { echo 'a,*'; }
    $ IFS=,
    $ set -o xtrace
    $ if [ $(cmd) ]; then echo yes; else echo no; fi
    ++ cmd
    ++ echo 'a,*'
    + '[' a = b ']'
    + echo no
    no
    

    Agora, [ "$(cmd)" ]faz um pouco mais de sentido. Colocar aspas em uma substituição de comando não evita a remoção de caracteres de nova linha à direita, ainda não é especificado quando cmdgera NULs, mas evita a remoção de split+glob e vazia. Portanto, [sempre serão passados ​​3 argumentos: [, a saída de cmddespojado de caracteres de nova linha à direita e ]. Com um argumento ao lado [e ], [retorna true se e somente se esse argumento não for uma string vazia, é o mesmo que [ -n "$(cmd)" ].

    Portanto, é como dizer: " cmdexibe pelo menos um caractere que não é um caractere de nova linha (ou NUL)" . Para comandos que produzem texto (o texto é garantido como sendo feito de linhas e não contém NULs), isso é " cmdexibe pelo menos uma linha não vazia?"

    Exceto pela saída curta, ele faz isso de maneira ineficiente, pois acabamos lendo toda a saída e armazenando na memória, e então passamos is para [.

    if cmd | grep -q .; then...
    

    Teria sido mais eficiente, pois grepsairia com true assim que encontrasse uma linha que contém pelo menos um caractere.

    Se cmdfosse yes, if [ "$(cmd)" ]acabaria travando o shell com um erro de memória insuficiente , enquanto if cmd | grep -q .a saída seria sim imediatamente (e yesseria encerrada por meio de um sinal SIGPIPE).

    Para testar se é cmdbem-sucedido, independentemente do que pode ou não produzir, basta fazer:

    if cmd; then
      echo cmd succeeded
    else
      echo cmd failed
    fi
    

    if $(cmd); thené outro exemplo de código que faz pouco sentido. Mas há uma pequena reviravolta no exemplo de cmdser false.

    Novamente, $(cmd)é retirado os caracteres de nova linha à direita, sujeitos a split+glob. Mas, desta vez, as palavras resultantes não são passadas para um utilitário [/ test, mas a primeira dessas palavras, se houver, é tratada como o comando a ser executado e todas as palavras passadas como argumentos para ele.

    Portanto, se cmdoutputs echo:hello:worlde $IFScontém :, isso acaba sendo executado echocom echo, helloe worldcomo argumentos e assumindo echoque consegue escrever com sucesso "hello world\n"em seu stdout, ele retornará true/success, então a thenparte será executada.

    Agora, se cmdnão produzir saída ou apenas caracteres de nova linha e caracteres de espaço em branco IFS, $(cmd)não resultará em nenhuma palavra, portanto, outro comando não será executado e, nesse ponto, é o status de saída de cmdsi mesmo que importará para a ifdeclaração.

    Em:

    if $(false); then
      echo yes
    else
      echo no
    fi
    

    noé saída porque, como falsenão produz saída, nenhum comando foi executado e é o status de saída falseque decide que a elsepeça seja executada.

    Em:

    if $(echo true; false); then
      echo yes
    else
      echo no
    fi
    

    e assumindo que $IFSnão contém t, r, unem ecaractere; trueacaba sendo executado e é seu status de saída que determina a ramificação na ifinstrução.

    • 11

relate perguntas

  • Como funciona este comando? mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | nc -l 1234 > /tmp/f

  • FreeBSD's sh: funções de lista

  • Existe uma maneira de fazer ls mostrar arquivos ocultos apenas para determinados diretórios?

  • o que grep -v grep faz

  • Como salvar um caminho com ~ em uma variável?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Possível firmware ausente /lib/firmware/i915/* para o módulo i915

    • 3 respostas
  • Marko Smith

    Falha ao buscar o repositório de backports jessie

    • 4 respostas
  • Marko Smith

    Como exportar uma chave privada GPG e uma chave pública para um arquivo

    • 4 respostas
  • Marko Smith

    Como podemos executar um comando armazenado em uma variável?

    • 5 respostas
  • Marko Smith

    Como configurar o systemd-resolved e o systemd-networkd para usar o servidor DNS local para resolver domínios locais e o servidor DNS remoto para domínios remotos?

    • 3 respostas
  • Marko Smith

    apt-get update error no Kali Linux após a atualização do dist [duplicado]

    • 2 respostas
  • Marko Smith

    Como ver as últimas linhas x do log de serviço systemctl

    • 5 respostas
  • Marko Smith

    Nano - pule para o final do arquivo

    • 8 respostas
  • Marko Smith

    erro grub: você precisa carregar o kernel primeiro

    • 4 respostas
  • Marko Smith

    Como baixar o pacote não instalá-lo com o comando apt-get?

    • 7 respostas
  • Martin Hope
    user12345 Falha ao buscar o repositório de backports jessie 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl Por que a maioria dos exemplos do systemd contém WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky Como exportar uma chave privada GPG e uma chave pública para um arquivo 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll status systemctl mostra: "Estado: degradado" 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim Como podemos executar um comando armazenado em uma variável? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S Por que /dev/null é um arquivo? Por que sua função não é implementada como um programa simples? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 Como ver as últimas linhas x do log de serviço systemctl 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - pule para o final do arquivo 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla Por que verdadeiro e falso são tão grandes? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis Substitua a string em um arquivo de texto enorme (70 GB), uma linha 2017-12-30 06:58:33 +0800 CST

Hot tag

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve