Comportamento dos mesmos comandos (em Bash) - executando um por um no console vs como script único

1

Rodando no OS X 10.11.1, eu executo a seguinte série de comandos (um por um), no console:

FILE="a b c.tiff"  # file in the current folder
VAR=$(mdls -name kMDItemContentCreationDate $FILE) # storing the creation time string  
TS=$(echo ${VAR[2]}; echo ${VAR[3]}  # saving the date and time only
echo $TS  

e a expansão funciona lindamente. A saída mostra:

2016-01-16 15:34:29

No entanto, quando eu os salvo em um script e o executo, parece que algo durante a avaliação é diferente.

A depuração (com bash -x ) produz:

FILE='a b c.tiff'
mdls -name kMDItemContentCreationDate a b c.tiff
VAR='a: could not find a.'
echo
echo
TS=
echo

, vejo a expansão se comportando de maneira diferente.

Minha preocupação é por que essa diferença ocorre e como devo corrigir meu script. Obrigado.

    
por elder elder 18.01.2016 / 20:00

1 resposta

2

Tenho certeza de que você não executou os comandos exatos em um console ou teria obtido o mesmo resultado. Há dois problemas sérios aqui e algumas práticas de script ruins. Problemas sérios primeiro:

  • Quando você se refere a uma variável (por exemplo, $FILE ) sem aspas duplas em torno dela, o shell a dividirá em "palavras" e, em seguida, expandirá todos os curingas antes de transmiti-la ao comando. Nesse caso, isso significa que a b c.tiff será dividido em "a", "b" e "c.tiff". É por isso que você está recebendo o "a: não conseguiu encontrar um". erro.

    Solução: você deve colocar referências de variável entre aspas duplas, a menos que queira especificamente a divisão de palavras e a expansão de curingas. (Há alguns casos em que deixar as aspas duplas é seguro, mas acompanhá-las é mais problemático do que vale a pena. Apenas adquira o hábito de usar aspas duplas.)

  • Quando você usa uma atribuição como VAR=$(somecommand) , ele atribui a variável como uma string simples, não uma matriz. Para armazená-lo como uma matriz, use parênteses no lado direito, como em VAR=( $(somecommand) ) . Observe que, como $(somecommand) não está entre aspas, ele será dividido por palavras e curinga, mas, nesse caso, queremos que a palavra seja dividida (para que cada palavra seja armazenada em um elemento de matriz separado) e o formato de saída é previsível o suficiente para que a expansão de curingas não faça nada de estranho para nos estragar. Então este é um dos raros casos em que deixar as aspas duplas é ok.

Com esses dois fixos, a segunda linha se torna:

VAR=( $(mdls -name kMDItemContentCreationDate "$FILE") )

Agora, para algumas coisas, há maus hábitos de script que não estão causando problemas aqui:

  • Na atribuição TS=$(echo ${VAR[2]}; echo ${VAR[3]}) (nota: adicionei o parêntese de fechamento ausente), a substituição do comando e os comandos echo não estão fazendo nada útil. O que isto faz é pegar os valores dos elementos do array, word-split e wildcard-expandi-los (o que não faz nada aqui), passá-los como parâmetros para echo , pegar a saída desses comandos e coletá-los em uma variável. É muito trabalho juntar duas cordas. Apenas use 'TS="$ {VAR [2]} $ {VAR [3]}".

    BTW, também está fazendo algo um pouco estranho: está colocando as cordas juntas com uma nova linha entre elas. Quando você imprime com echo $TS , (novamente) obtém divisão de palavras, então cada linha é tratada como um argumento separado para echo , que cola espaços entre os argumentos. Resultado líquido: o comando echo está convertendo efetivamente a nova linha em um espaço. Seria muito mais limpo tornar um espaço em primeiro lugar, e depois citar duas vezes quando você usá-lo, então o seu script não depende de dois comportamentos estranhos que se anulam mutuamente.

  • Finalmente, usar variáveis de maiúsculas não é muito seguro. Há um número de variáveis all-caps que têm um significado especial para o shell (e alguns comandos), e se você acidentalmente usar um deles, você pode obter resultados estranhos. O exemplo clássico é atribuir algo ao PATH, quando todos os comandos se tornam repentinamente desconhecidos. É difícil manter o controle de todas as variáveis mágicas, portanto, basta usar variáveis minúsculas para suas coisas e você estará seguro.

Com tudo isso limpo, aqui está o que recebo para o script:

file="a b c.tiff"  # file in the current folder
var=( $(mdls -name kMDItemContentCreationDate "$file") ) # storing the creation time string as an array
ts="${var[2]} ${var[3]}"  # saving the date and time only
echo "$ts"

Nota final: quando em dúvida, execute o seu script através do shellcheck.net - ele indicará muitos dos erros padrão do iniciante e poupe muito tempo!

    
por 19.01.2016 / 05:45