Por que o {} não funciona para este comando?

0

Não tenho certeza sobre a diferença entre {} e () .
No snippet a seguir:

#!/bin/bash
set -e
echo "two">file.txt
ARRAY=(one two three)
rc=0
for i in ${ARRAY[@]}; do
    echo "grepping $i "
    #grep "$i" file.txt || (echo "failed grep" && exit 1) <--1
    #grep "$i" file.txt || {rc=$? && echo "failed grep"}  <--2
    grep "$i" file.txt || (rc=$? && echo "failed grep")   <--3
done

exit $rc  

O comando 2 é o único que não funciona. Mas eu estava esperando que isso funcionasse e também que o comando 1 não terminaria o script de acordo com a resposta aceita aqui
Alguém pode explicar o que está acontecendo?

    
por Jim 11.10.2017 / 13:16

2 respostas

1

Em bash e a maioria dos shells POSIX, enquanto ( e ) são caracteres de token especiais na sintaxe do shell (eles são tratados especialmente onde quer que apareçam, incluindo como parte de tokens de vários caracteres como (( , $( , <( , $(( , <#(( ...), { e } não são exceções quando combinadas com caracteres de token especiais, como em ${ , ou são usadas na% especial{x,y} ou {x..y} form.

Você notará que, enquanto echo ) der um erro, echo } não e sairá com } , portanto, o } em {echo} não poderá fechar o { .

Especificamente, { e } são palavras-chave como while , time , ! . Eles só podem ocorrer onde um comando é esperado (em uma primeira aproximação) e somente quando uma palavra separada.

{echo seria o comando {echo , não a palavra-chave { seguida por echo . Assim como while[ seria o comando while[ e não a palavra-chave while seguida por [ .

Você pode ter { echo;} ou {<file cat;} em que { e } estão delimitados e na posição de comando, mas não {echo} .

Existem algumas exceções. Você perceberá que {(echo test)} também funciona, embora nada seja normalmente esperado após esse fechamento ) , exceto os redirecionamentos, e que } não esteja na posição de comando. O mesmo para { { echo; } } , em que o segundo } não está na posição de comando.

{ echo; ! } ou { echo; time } trabalham em ksh93, mas não em bash (embora esses } estejam na posição de comando.

Uma exceção a isso é zsh que tenta reconhecer { e } como tokens quando possível. Isto é, quando em posição de comando e em alguns casos limitados. {echo} funciona lá, mas {{echo}} não faz por exemplo (apesar de {{echo} } ). {echo,foo} é como { echo,foo;} lá, em vez de echo foo de bash / ksh.

<file {head;head} funciona em zsh enquanto não funciona em outros shells.

Isso é controlado pelas opções IGNORE_BRACES e IGNORE_CLOSE_BRACES .

    
por 11.10.2017 / 14:16
1

O {} é um espaço reservado para instruções compostas em um formato separado por dois pontos em bash e () cria um contexto de sub-shell sob o qual as atualizações de variáveis não são refletidas fora das chaves.

Você nem precisa mexer com os valores $? do código de retorno ao usar grep ou qualquer outro comando do shell. Você pode usar diretamente seu código de saída no if-condicional, suprimindo a saída do console (geralmente os comandos retornam 0 em caso de sucesso do comando). Em grep você pode fazer isso ativando o -q flag

if grep -q "$i" file.txt; then
    printf "grep suceeded\n"
else
    printf "grep failed\n"
fi

Se você quiser armazenar o código de saída manualmente, a idéia de usar uma instrução composta está correta (usar o sub-shell não funcionará aqui!). Você só precisa terminar a instrução composta com um ;

grep "$i" file.txt || {rc=$? ; echo "failed grep" ; }

Agora, o código de saída está disponível em rc para você usar com. Ou mais simplesmente você pode simplesmente usar a variável no próprio eco

grep "$i" file.txt || echo "failed grep with rc: $?"

Para adicionar à resposta usando sub-shells () , para armazenar o contexto da variável não é uma boa prática. As conchas são terminadas logo após o término dos comandos fechados.

Por exemplo Usando esta operação de igualdade de cadeia simples abaixo

str='foo'; rc=0
[[ $str == *doo* ]] || { rc=$?; echo "string comparison failed" ; }
echo "$rc"

irá refletir o valor de rc corretamente, mas o mesmo não funcionará no caso de um sub-shell como

str='foo'; rc=0
[[ $str == *doo* ]] || ( rc=$?; echo "string comparison failed" )
echo "$rc"

irá refletir o valor a ser 0 quando deveria retornar 1 set dentro da sub-camada.

[Não relevante para a questão atual]

Você deve sempre citar suas expansões de matriz. Não fazer isso dividirá o elemento da matriz contendo espaços ou outros caracteres especiais como palavras separadas, em vez de tratá-los como um

for i in "${ARRAY[@]}"; do
    
por 11.10.2017 / 13:21