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 / 490393
Accepted
Kalle Richter
Kalle Richter
Asked: 2018-12-22 12:28:29 +0800 CST2018-12-22 12:28:29 +0800 CST 2018-12-22 12:28:29 +0800 CST

Qual é o propósito de adicionar um prefixo em ambos os lados de uma comparação de variável de shell com um literal de string?

  • 772

Eu encontrei comparações de variáveis ​​com literais de string várias vezes ao longo dos anos que tinham um caractere prefixando a variável e o literal, por exemplo

if [ "x$A" = "xtrue" ]; then

para verificar se $Aé "true".

Presumo que isso seja feito para obter compatibilidade com o shell ou para contornar um bug de longo prazo, um comportamento não intuitivo, etc. Nada óbvio vem à mente.

Hoje imaginei que queria saber o motivo, mas minha pesquisa não deu em nada. Ou talvez seja apenas eu fazendo algo de uma exposição bastante frequente a ocorrências raras.

Essa prática ainda é útil, talvez até melhor?

shell variable
  • 2 2 respostas
  • 951 Views

2 respostas

  • Voted
  1. Best Answer
    Stéphane Chazelas
    2018-12-23T04:34:38+08:002018-12-23T04:34:38+08:00

    O importante a entender aqui é que na maioria dos shells¹, [é apenas um comando comum analisado pelo shell como qualquer outro comando comum.

    Em seguida, o shell invoca esse comando [(também conhecido como test) com uma lista de argumentos e, em seguida, cabe [a interpretá-los como uma expressão condicional.

    Nesse ponto, essas são apenas uma lista de strings e as informações sobre quais resultaram de alguma forma de expansão são perdidas, mesmo nos shells em que [está embutido (todos do tipo Bourne atualmente).

    O [utilitário costumava ter dificuldade em dizer quais de seus argumentos eram operadores e quais eram operandos (a coisa em que os operadores trabalham). Não ajudou que a sintaxe fosse intrinsecamente ambígua. Por exemplo:

    • [ -t ]costumava ser (e ainda é em alguns shells/ [s) para testar se stdout é um terminal.
    • [ x ]é a abreviação de [ -n x ]: test se xé uma string não vazia (para que você possa ver que há um conflito com o acima).
    • em alguns shells/ [s, -ae -opode ser tanto unário ( [ -a file ]para arquivo acessível (agora substituído por [ -e file ]), [ -o option ]for a opção está habilitada? ) e operadores binários ( e e ou ). Novamente, ! -a xpode ser and(nonempty("!"), nonempty("x"))ou not(isaccessible("x")).
    • (e adicione mais problemas ).!

    Em linguagens de programação normais como C ou perl, em:

    if ($a eq $b) {...}
    

    Não há como o conteúdo de $aou $bser considerado como operadores porque a expressão condicional é analisada antes deles $ae $bé expandida. Mas em conchas, em:

    [ "$a" = "$b" ]
    

    O shell expande as variáveis ​​primeiro ². Por exemplo, se $acontém (e $bcontém ), tudo o que o [comando vê são [, (, =e argumentos ). ]Então isso significa "(" = ")"(são (e )lexicalmente iguais) ou ( -n = )(é =uma string não vazia).

    As implementações históricas ( testapareceram no Unix V7 no final dos anos 70) costumavam falhar mesmo nos casos em que não eram ambíguas apenas por causa da ordem em que estavam processando seus argumentos.

    Aqui com a versão 7 Unix em um emulador PDP11:

    $ ls -l /bin/[
    -rwxr-xr-x 2 bin      2876 Jun  8  1979 /bin/[
    $ [ ! = x ]
    test: argument expected
    $ [ "(" = x ]
    test: argument expected
    

    A maioria dos shells e [implementações têm ou tiveram problemas com essas ou suas variantes . Com bash4.4 hoje:

    bash-4.4$ a='(' b=-o c=x
    bash-4.4$ [ "$a" = "$b" -o "$a" = "$c" ]
    bash: [: `)' expected, found =
    

    POSIX.2 (publicado no início dos anos 90) desenvolveu um algoritmo que tornaria [o comportamento de 's inequívoco e determinístico quando passado no máximo 4 argumentos (ao lado de [e ]) nos padrões de uso mais comuns ( [ -f "$a" -o "$b" ]ainda não especificados, por exemplo). Ele descontinuou (, ), -ae -o, e foi descartado -tsem operando. bashimplementou esse algoritmo (ou pelo menos tentou) em bash2.0.

    [Assim, em implementações compatíveis com POSIX , [ "$a" = "$b" ]é garantido comparar o conteúdo de $ae $bpara igualdade, sejam eles quais forem. Sem -o, escreveríamos:

    [ "$a" = "$b" ] || [ "$a" = "$c" ]
    

    Ou seja, chame [duas vezes, cada vez com menos de 5 argumentos.

    Mas demorou um pouco para que todas as [implementações se tornassem compatíveis. bash's não era compatível até 4.4 (embora o último problema fosse para o [ '(' ! "$var" ')' ]qual ninguém realmente usaria na vida real)

    O /bin/shdo Solaris 10 e anterior, que não é um shell POSIX, mas um shell Bourne ainda tem problemas com [ "$a" = "$b" ]:

    $ a='!' b='!'
    $ [ "$a" = "$b" ]
    test: argument expected
    

    O uso [ "x$a" = "x$b" ]contorna o problema, pois não há [operador que comece com x. Outra opção é usar caseem vez disso:

    case "$a" in
      "$b") echo same;;
         *) echo different;;
    esac
    

    (citar é necessário em torno $bde , não em torno de $a).

    De qualquer forma, não é e nunca foi sobre valores vazios. As pessoas têm problemas com valores vazios [quando se esquecem de citar suas variáveis, mas isso não é um problema [.

    $ a= b='-o x'
    [ $a = $b ]
    

    com o valor padrão de $IFSse torna:

    [ = -o x ]
    

    Que é um teste para saber se é =ou xnão uma string não vazia, mas nenhuma quantidade de prefixação ajudará³ como [ x$a = x$b ]ainda será: [ x = x-o x ]o que causaria um erro e poderia ficar muito pior incluindo DoS e injeção de comandos arbitrários com outros valores como em bash:

    bash-4.4$ a= b='x -o -v a[`uname>&2`]'
    bash-4.4$ [ x$a = x$b ]
    Linux
    

    A solução correta é sempre citar :

    [ "$a" = "$b" ]   # OK in POSIX compliant [ / shells
    [ "x$a" = "x$b" ] # OK in all Bourne-like shells
    

    Observe que exprtem problemas semelhantes (e ainda piores).

    exprtambém tem um =operador, embora seja para testar se os dois operandos são inteiros iguais quando se parecem com números inteiros decimais, ou ordenam o mesmo quando não.

    Em muitas implementações, expr + = +, ou expr '(' = ')'ou expr index = indexnão fazem comparação de igualdade. expr "x$a" = "x$b"funcionaria em torno disso para comparação de strings, mas prefixar com um xpoderia afetar a classificação (em localidades que têm elementos de agrupamento começando com x, por exemplo) e obviamente não pode ser usado para comparação de números expr "0$a" = "0$b"não funciona para comparar inteiros negativos. expr " $a" = " $b" funciona para comparação de inteiros em algumas implementações, mas não em outras (para a=01 b=1, alguns retornariam true, alguns false).


    ¹ ksh93é uma exceção. In ksh93, [pode ser visto como uma palavra reservada em que na [ -t ]verdade é diferente de var=-t; [ "$var" ], ou de ""[ -t ]ou cmd='['; "$cmd" -t ]. Isso é para preservar a compatibilidade com versões anteriores e ainda ser compatível com POSIX nos casos em que isso importa. O -tsó é considerado um operador aqui se for literal e ksh93detecta que você está chamando o [comando.

    ² ksh adicionou um [[...]]operador de expressão condicional com suas próprias regras de análise de sintaxe (e alguns problemas próprios) para resolver isso (também encontrado em alguns outros shells, com algumas diferenças).

    ³ exceto zshonde split+glob não é invocado na expansão do parâmetro, mas a remoção vazia ainda é, ou em outros shells ao desabilitar split+glob globalmente comset -o noglob; IFS=

    • 17
  2. JdeBP
    2018-12-22T14:25:01+08:002018-12-22T14:25:01+08:00

    As pessoas geralmente atribuem o prefixo a problemas com strings vazias, mas essa não é a razão para isso. O problema é muito simples: a expansão da variável pode ser um dos testoperadores de ', transformando subitamente um teste de igualdade binária em uma expressão diferente.

    Implementações recentes do comando na maioria das plataformas evitam a armadilha com look-ahead no analisador de expressão, evitando que o analisador reconheça o primeiro operando de um operador binário como algo diferente de um operando, desde que haja tokens suficientes para ser um binário operador claro:

    % a=-n
    % /bin/teste "$a" = -n ; eco $?
    0
    % /bin/teste "$a" = ; eco $?
    0
    % /bin/teste x"$a" = ; eco $?
    teste: =: argumento esperado
    2
    %a='('
    % /bin/teste "$a" = "(" ; echo $?
    0
    % /bin/teste "$a" = ; eco $?
    teste: parêntese de fechamento esperado
    2
    %

    • 7

relate perguntas

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

  • Bash ou condição em uma instrução while

  • 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

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

    • 4 respostas
  • Marko Smith

    ssh Não é possível negociar: "nenhuma cifra correspondente encontrada", está rejeitando o cbc

    • 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

    Como descarregar o módulo do kernel 'nvidia-drm'?

    • 13 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
    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
    Wong Jia Hau ssh-add retorna com: "Erro ao conectar ao agente: nenhum arquivo ou diretório" 2018-08-24 23:28:13 +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
  • Martin Hope
    Bagas Sanjaya Por que o Linux usa LF como caractere de nova linha? 2017-12-20 05:48:21 +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