Como exatamente “/ bin / [” funciona?

50

Sempre fico surpreso que na pasta /binexista um [programa.

É assim que se chama quando estamos fazendo algo como if [ something ]:?

Ao chamar o [programa explicitamente em um shell, ele solicita uma correspondência ]e, quando eu forneço o colchete de fechamento, ele parece não fazer nada, não importa o que eu insira entre os colchetes.

Desnecessário dizer que a maneira usual de obter ajuda sobre um programa não funciona, ou seja, man [nem [ --helpfunciona.

Bregalad
fonte
11
@IporSircer [refere-se ao testcomando, porém, não expr, assim que deve serman test
Sergiy Kolodyazhnyy
8
man '['funciona bem para mim - ou você esqueceu de citar [ou tem uma definição diferente de "funciona".
perfil completo de Toby Speight
3
Alguns avisos: [avalia seus argumentos, portanto, você precisa ter espaços entre todos eles. [ a=b ]não é uma comparação: sempre será verdadeira (é uma única sequência: "a = b", que é sempre avaliada como verdadeira ) E você deve limitar o número de argumentos a 4 (mesmo que implementações recentes permitam mais .. por exemplo, limita a 4, por exemplo: [ "a" = "b" ]já possui 4 argumentos: "a" "=" "b" e o final desnecessário do teste arg: "]"). Se você precisar de mais: testes em cadeia com, por exemplo:if [ "$Var_a" = "foo" ] && [ "$Var_b" = "bar" ] ; then : do something ; fi
Olivier Dulac
11
O @OlivierDulac a !por si só (sem escape e sem aspas) não será substituído, e melhor ainda if ! [ .... Todas as expansões festança que usam !incluir pelo menos em outro personagem
Muru
3
Você também deve observar que também existe um [-builtin bash(e possivelmente outros reservatórios), e que isso pode ser usado em vez de /bin/[. Também existe o testcomando-, que em muitos sistemas é um link simbólico para /bin/[(ou vice-versa) - mas em outros é um comando separado.
Baard Kopperud

Respostas:

63

O [trabalho do comando é avaliar expressões de teste. Ele retorna com um status de saída 0 (que significa verdadeiro ) quando a expressão é resolvida como verdadeira e outra coisa (que significa falsa ) caso contrário.

Não é que não faça nada, é apenas que seu resultado pode ser encontrado em seu status de saída. Em um shell, você pode descobrir sobre o status de saída do último comando em $?shells semelhantes a Bourne ou $statusna maioria dos outros shells (fish / rc / es / csh / tcsh ...).

$ [ a = a ]
$ echo "$?"
0
$ [ a = b ]
$ echo "$?"
1

Em outros idiomas perl, o status de saída é retornado, por exemplo, no valor de retorno de system():

$ perl -le 'print system("[", "a", "=", "a", "]")'
0

Observe que todos os shells modernos do tipo Bourne (e fish) possuem um [comando interno. Aquele em /binque normalmente seria executado somente quando você usa outro shell ou quando faz coisas como env [ foo = bar ]ou find . -exec [ -f {} ] \; -printou que perlcomandam acima ...

O [comando também é conhecido pelo testnome. Quando chamado como test, não requer um ]argumento de fechamento .

Embora seu sistema possa não ter uma página de manual [, provavelmente tem uma para test. Mas, novamente, nota que seria documentar a /bin/[ou /bin/testimplementação. Para saber sobre o [built-in no seu shell, você deve ler a documentação do seu shell.

Para obter mais informações sobre o histórico desse utilitário e a diferença com a [[...]]expressão de teste ksh, dê uma olhada nessas outras perguntas e respostas aqui .

Stéphane Chazelas
fonte
Bom, como sempre. Você pode adicionar os comentários que adicionei abaixo da pergunta @Bregalad? ou talvez alguns extras? Dessa forma, a resposta será ainda mais completa em "exatamente como ele funciona" (I note que você já deu uma mais informações acurate sobre "]" do que eu fiz ^^)
Olivier Dulac
“Todas as conchas do tipo Bourne (e peixes) têm um [comando” embutido. Isso é verdade na prática no século 21, mas tenho certeza de que usei uma concha sem [. Não me lembro se era uma variante Bourne ou uma versão antiga do ash.
Gilles 'SO- stop be evil' ''
@Gilles The Bourne shell implementada tanto [e testcomo builtins com Unix System III. Antes disso, não havia [e testhavia apenas um comando binário externo.
Jlliagre
@Gilles, o ash original tinha teste embutido (mesclado com expr), mas opcionalmente . De acordo com in-ulm.de/~mascheck/various/ash , os BSDs não tinham teste embutido até por volta de 2000. Adicionei "modern".
Stéphane Chazelas
Por que diabos você gostaria de fazer find . -exec [ f {} ] \; -print? O comando find . -type f -printfaria a mesma coisa com muito mais eficiência ...
Este não é o meu nome verdadeiro
41

Sempre fico surpreso que na pasta /binexista um [programa.

Você está certo em ser surpreendido. Esse é um dos raros comandos POSIX, com o utilitário nulo ( :), que não respeita a convenção de caracteres permitidos do arquivo de comandos (conjunto de caracteres do nome do arquivo portátil).

É assim que se chama quando estamos fazendo algo como if [ something ]:?

Precisamente, mas pode ser usado sem o iftambém.

Ao chamar o [programa explicitamente em um shell, ele solicita uma correspondência ]e, quando eu forneço o colchete de fechamento, ele parece não fazer nada, não importa o que eu insira entre os colchetes.

Ele não faz nada visível, mas na verdade faz a mesma coisa do que quando usado if, ou seja, define o status de saída como 0 (verdadeiro) ou qualquer outra coisa (falso), dependendo do que você coloca dentro dos colchetes. É (por uma razão) o mesmo comportamento que o testcomando; a única diferença é que ele procura o final ]. Veja man testpara detalhes.

Desnecessário dizer que a maneira usual de obter ajuda sobre um programa não funciona, ou seja, man [nem [ --helpfunciona.

Isso depende do seu sistema operacional. man [definitivamente funciona para mim em algumas distribuições principais do Gnu / Linux, mas não no Solaris.

[ --helppode funcionar ou não, dependendo da implementação, pois está quebrando a sintaxe de qualquer maneira, perdendo o final ]. Além disso, o padrão POSIX para o test/ [comando exclui explicitamente todas as opções, incluindo a --rescisão opção para ambos [ --help ]e test --helpnecessidade de voltar truee ficar em silêncio por design. Note-se que o que você colocar dentro dos colchetes ou depois [e que parecem opções (por exemplo -f file, -n stringe os gostos) não são opções , mas operandos .

Todos os intérpretes de shell estilo moderno Bourne (como bash, ksh, dashe zshpara citar alguns) implementar o test/ [utilidade internamente como um builtin assim quando você usá-los, a página do manual para a direita para se referir pode ser o único do shell, e não a testum.

Antes do Unix System III (1981), o shell Bourne não implementava o testutilitário como um componente interno; portanto, apenas a implementação do comando binário externo estava disponível. Não havia um [comando (interno ou interno) até o Unix System III; por exemplo, no Unix Versão 7, você tinha que digitar:

if test something ; then

ao invés de:

if [ something ] ; then
jlliagre
fonte
"man [definitivamente funciona para mim nas distribuições mainstream do Gnu / Linux" Hmm .. Centos (reconhecidamente 6.8 ainda) claramente não é mainstream o suficiente :) man testfunciona, mas não man [ Por outro lado, meu ambiente cygwin conhece man [!
Adam
11
@ Adam Sim, você está certo, isso é mais recente do que eu pensava, embora não tenha escrito todas as distribuições principais do Linux, apenas que funcionou para mim (ou seja, nas que testei). Esse é o clone OL 7.2 (RHEL 7.2) e o Mint 17.1 (Ubuntu 14.04).
Jlliagre
man [parece não funcionar no Manjaro (baseado no Arch Linux). O manual para testlistas [ --helpcomo uma invocação válida que deve mostrar ajuda, mas, curiosamente, não, e queixa-se de uma falta ], o mesmo que com --version. Adicionando o ]não faz nada, por isso parece impossível obter ajuda diferente de man test.
precisa saber é o seguinte
@Darkhogg Você pode tentar /usr/bin/[ --helpou /bin/[ --help.
Jlliagre
11
@ jlliagre Você está completamente certo, eu estava usando os componentes internos. env [ --helpalém de /usr/bin/[ --helpfuncionar completamente bem (embora eu precise citar /usr/bin/[ou zsh reclamar).
precisa saber é o seguinte
10

[é realmente mais conhecido como testcomando. O uso típico desse comando é simplesmente avaliar expressões e retornar sua condição - verdadeira ou falsa. É frequentemente usado em if-then-else-fiinstruções, embora possa ser usado fora das ifinstruções para executar condicionalmente outros comandos por meio de shell &&ou ||operadores, assim.

$ [ -e /etc/passwd  ] && echo "File exists"
File exists

$ test -e /etc/passwd && echo "File exists"
File exists

Mais especificamente, a avaliação é comunicada a outros comandos via status de saída. Alguns programas podem optar por emitir o status de saída para significar diferentes tipos de eventos - conclusão do programa com êxito, um erro de tipo específico ocorrendo durante a execução ou erros de sintaxe. No caso do testcomando, existem 0significa verdadeiro e 1falso. Como apontou Stephan, os erros de sintaxe produzem o status de saída de 2.

Sua localização depende do seu sistema e também explica por que você não viu a página de manual quando o viu man [. Por exemplo, no FreeBSD está abaixo /bin. No Linux (ou no meu caso em particular, Ubuntu 16.04), ele está dentro /usr/bin/. Se você faz man [ou man testem um sistema Linux, verá a mesma documentação aberta. Também é importante observar que seu shell pode ter sua própria implementação test.

Também deve ser observado que esse comando tem problemas que a implementação do shell Korn (comumente conhecida como referência de "expressão condicional" com colchetes duplos [[ "$USER" = "root" ]])) procura resolver. Esse recurso também é usado por outros shells, como bashe zsh.

Sergiy Kolodyazhnyy
fonte
/usr/bin/para o Amazon Linux também.
21417 franklinsijo
@ StéphaneChazelas Obrigado. Resposta editada em conformidade
Sergiy Kolodyazhnyy 11/01
3

Nem man [nem [ --helpobras

Em algumas distros (por exemplo, Ubuntu), man [segue um link simbólico para test(1). Em outros (por exemplo, Arch), este não é o caso. (Mas a testpágina do manual também documenta o uso [)


type -a [ mostra que há um shell embutido e um executável.

$ type -a [
[ is a shell builtin
[ is /usr/bin/[

bash-builtin [ --helpapenas imprime uma mensagem de erro. (Mas como é um builtin, você pode usar help [ou consultar a página de manual do bash / docs).

/usr/bin/[ --help imprime a saída completa da ajuda (para a versão GNU Coreutils), que começa com:

$ /usr/bin/[ --help
Usage: test EXPRESSION
  or:  test
  or:  [ EXPRESSION ]
  or:  [ ]
  or:  [ OPTION
Exit with the status determined by EXPRESSION.

      --help     display this help and exit
      --version  output version information and exit

e, em seguida, descreve a sintaxe permitida para EXPRESSION.

Essa é outra maneira de descobrir isso [e testser equivalente.


BTW, se você está programando para bash (ou escrevendo one-liners interativamente), eu recomendaria em [[vez de [. É melhor de várias maneiras, veja os links na resposta de Serg.

Peter Cordes
fonte
2

[O comando retorna status de saída zero se a expressão contida em seus argumentos for considerada verdadeira e status de saída diferente de zero se expressão contida em seus argumentos for considerada falsa. Ele também falha com a mensagem de erro se seu último argumento não for ](isso é feito puramente por razões estéticas).

Por exemplo:

[ hello ]
echo "Exit-status of [ hello ] is:" $?
[ abc = abc ]
echo "Exit-status of [ abc = abc ] is:" $?
[ ]
echo "Exit-status of [ ] is:" $?
[ abc = def ]
echo "Exit-status of [ abc = def ] is:" $?

… Produzirá:

O status de saída de [olá] é: 0       - porque a seqüência de caracteres não vazia é considerada verdadeira O 
status de saída de [abc = abc] é: 0   - porque 'abc' é realmente o mesmo que 'abc' O 
status de saída de [] é : 1             - porque a cadeia vazia é considerada falsa O 
status de saída de [abc = def] é: 1   - porque 'abc' realmente difere de 'def'

No entanto, o bash e muitos outros shells geralmente não invocam /bin/[(ou /usr/bin/[) nesses casos, mas chamam o comando interno com exatamente o mesmo comportamento (apenas por razões de desempenho). Para chamar /bin/[(não o substituto interno do shell), é necessário especificar explicitamente seu caminho (por exemplo /bin/[ hello ], você não precisa prefixar o nome do ]diretório embora ☺ ) ou configurar o shell para não usar um substituto interno (por exemplo, enable -n [na festança).

PS: Como foi dito em outras respostas, [está relacionado a test. Mas test, diferentemente [, não exige ]como seu último argumento (e não o espera; adicionar acréscimos ]a testargumentos pode causar falhas na mensagem de erro ou retornar resultados incorretos) . O /bin/teste /bin/[pode resolver para o mesmo arquivo (por exemplo, um está vinculado ; nesse caso, o desvio de comportamento provavelmente é implementado pela análise do comando atualmente chamado no próprio código test/ [) ou para arquivos diferentes. Para test, o shell também geralmente chama substituto interno, a menos que o caminho seja especificado explicitamente ( /bin/test) ou esteja configurado para não fazer isso (enable -n test)

PPS: Ao contrário teste [, moderno ifnão é um arquivo real. Faz parte da sintaxe do shell (por exemplo, bash): if commandA; then commandB; fi(novas linhas podem ser usadas em vez de ponto e vírgula) faz commandBcom que seja executado se e somente se for commandAencerrado com status zero. Isso se encaixa perfeitamente ao comportamento de testou [, permitindo combiná-los como if [ "$a" = foo ]; then …; fi (ou if test "$a" = foo; then …; fi- apenas menos legíveis) . No entanto, scripts modernos costumam usar em [[vez de testou [, o que (como if) nunca é um arquivo real, mas sempre faz parte da sintaxe do shell.

PPPS: Quanto a man- nunca espere manter um artigo sobre todos os comandos do seu sistema de arquivos. Informações sobre algumas informações (mesmo "real",-file com base) comandos podem estar faltando, em alguns shell built-ins talvez presente não só dentro de um artigo dedicado a shell específico (que é o lugar onde você certamente vai encontrar informações sobre test, [, if, [[). Ainda assim, muitas distribuições têm artigos explícitos manpara teste [. (Sobre --help, não é reconhecido testpor uma razão óbvia: ele precisa lidar com casos silenciosos como a=--help; test "$a"; em algumas distribuições [ --help(sem fechar ]) ainda mostra ajuda, em outras não).

sasha
fonte
3
Observe que iffoi um comando até o Unix V6. O Unix V7 (1979) veio com o shell Bourne (com sua if-then-else-ficonstrução) e o novo testcomando.
Stéphane Chazelas