Como posso atribuir a saída de um comando a uma variável shell?

66

Eu quero atribuir o resultado de uma expressão a uma variável e concatená-la com uma string, depois ecoá-la. Aqui está o que eu tenho:

#!/bin/bash
cd ~/Desktop;
thefile= ls -t -U | grep -m 1 "Screen Shot";
echo "Most recent screenshot is: "$thefile;

Mas isso gera:

Screen Shot 2011-07-03 at 1.55.43 PM.png
Most recent screenshot is: 

Parece que não está sendo atribuído a $thefile e está sendo impresso conforme é executado.

    
por Nathan G. 03.07.2011 / 23:15

2 respostas

96

Uma designação de shell é uma única palavra, sem espaço após o sinal de igual. Então, o que você escreveu atribui um valor vazio a thefile ; além disso, como a atribuição é agrupada com um comando, ela transforma thefile em uma variável de ambiente e a atribuição é local nesse comando específico, ou seja, somente a chamada para ls vê o valor atribuído.

Você deseja capturar a saída de um comando, portanto, é necessário usar a substituição de comando :

thefile=$(ls -t -U | grep -m 1 "Screen Shot")

(Algumas literaturas mostram uma sintaxe alternativa thefile='ls …' ; a sintaxe backquote é equivalente à sintaxe entre parênteses e dolar, exceto que a citação dentro de backquotes é estranha às vezes, então use apenas $(…) .)

Outras observações sobre o seu script:

  • A combinação de -t (classificar por tempo) com -U (não classificar) não faz sentido; use apenas -t .
  • Em vez de usar grep para corresponder a capturas de tela, fica mais claro passar um caractere curinga a ls e usar head para capturar o primeiro arquivo:

    thefile=$(ls -t *"Screen Shot"* | head -n 1)
    
  • Geralmente, é uma má idéia analisar a saída de ls . Isso pode falhar bastante se você tiver nomes de arquivos com caracteres não imprimíveis. No entanto, classificar arquivos por data é difícil sem ls , portanto, é uma solução aceitável se você souber que não terá caracteres não imprimíveis ou barras invertidas nos nomes de arquivos.

  • Sempre use aspas duplas em torno de substituições de variáveis , ou seja, aqui escreva

    echo "Most recent screenshot is: $thefile"
    

    Sem aspas duplas, o valor da variável é reexpandido, o que causará problemas se contiver espaços em branco ou outros caracteres especiais.

  • Você não precisa de ponto-e-vírgula no final de uma linha. Eles são redundantes, mas inofensivos.
  • Em um script de shell, geralmente é uma boa ideia incluir set -e . Isso diz ao shell para sair se algum comando falhar (retornando um status diferente de zero).

Se você tiver o GNU find (em particular se estiver executando o Linux ou o Cygwin não embarcado), há outra abordagem para localizar o arquivo mais recente: ter find listar os arquivos e suas datas e usar sort e tail para extrair o arquivo mais novo.

thefile=$(find -maxdepth 1 -type f -name "*Screen Shot*" -printf "%T@ %p" |
          sort -k 1n | tail -n 1)

Se você estiver disposto a escrever este script em zsh em vez de bash, há uma maneira muito mais fácil de capturar o arquivo mais recente, porque zsh tem qualificadores de globo que permitem correspondências curinga não apenas em nomes, mas também em metadados de arquivos. A parte (om[1]) após o padrão é os qualificadores glob; om ordena as correspondências aumentando a idade (ou seja, pelo tempo de modificação, o mais recente primeiro) e [1] extrai apenas a primeira correspondência. A correspondência inteira precisa estar entre parênteses porque é tecnicamente uma matriz, já que globbing retorna uma lista de arquivos, mesmo que [1] signifique que, nesse caso específico, a lista contém (no máximo) um arquivo.

#!/bin/zsh
set -e
cd ~/Desktop
thefile=(*"Screen Shot"*(om[1]))
echo "Most recent screenshot is: $thefile"
    
por 03.07.2011 / 23:52
2

Se você quiser fazer isso com múltiplas linhas / múltiplos comandos / s, você pode fazer isso:

output=$( bash <<EOF
#multiline/multiple command/s
EOF
)

Ou:

output=$(
#multiline/multiple command/s
)

Exemplo:

#!/bin/bash
output="$( bash <<EOF
echo first
echo second
echo third
EOF
)"
echo "$output"

Saída:

first
second
third
    
por 01.06.2015 / 19:59