Shell Script: criando uma variável com opções dentro

11

Eu tenho um comando rsync com os seguintes parâmetros:

rsync -avz --{partial,stats,delete,exclude=".*"}

Eu quero colocar esses parâmetros dentro de uma variável para reutilizá-la depois no script. Algo assim:

#!/bin/bash
VAR=rsync -avz --{partial,stats,delete,exclude=".*"}
$VAR /dir1 /dir2

Eu tentei com aspas, aspas simples, colchetes, sem sucesso.

Kellyson
fonte
Pergunta semelhante no SO: stackoverflow.com/questions/13365553/…
Barmar
Tendo percorrido esse caminho antes: Verifique se a sequência de comandos final resultante não possui nenhuma sequência vazia. Se isso acontecer, o rsync poderá usá-los como parâmetros e, como são invisíveis, é muito difícil depurar. Eu tinha um primeiro parâmetro vazio e o rsync o interpretou como incluindo o diretório atual nas fontes de coisas a serem copiadas.
313 Joe

Respostas:

12

Colocar um comando complexo em uma variável nunca é uma abordagem recomendada. Veja BashFAQ / 050 - Estou tentando colocar um comando em uma variável, mas os casos complexos sempre falham!

Seu requisito se torna realmente simples, se você decidir usar uma função em vez de uma variável e passar argumentos para ela.

Algo como

rsync_custom() {
    [ "$#" -eq 0 ] && { printf 'no arguments supplied' >&2; exit 1 ; }
    rsync -avz --{partial,stats,delete,exclude=".*"} "$@"
}

e agora passe os argumentos necessários para ele como

rsync_custom /dir1 /dir2

A definição da função é bem simples, de certa forma, primeiro verificamos a contagem de argumentos de entrada usando a variável $#que não deve ser zero. Emitimos uma mensagem de erro dizendo que nenhum argumento é fornecido. Se houver argumentos válidos, "$@"representa os argumentos reais fornecidos à função.

Se esta é uma função que você usaria com bastante frequência, ou seja, também em scripts / linha de comando, adicione-a aos arquivos de inicialização do shell .bashrc, .bash_profilepor exemplo.

Ou, como observado, pode valer a pena expandir a expansão de chaves para separar args para uma melhor legibilidade, como

rsync_custom() {
    [ "$#" -eq 0 ] && { printf 'no arguments supplied' >&2; exit 1 ; }
    rsync -avz --partial --stats --delete --exclude=".*" "$@"
}
Inian
fonte
5
VAR=rsync -avz --{partial,stats,delete,exclude=".*"}

Isso tenta executar o comando -avzcom argumentos --partial, --statsetc. e com VARdefinido rsyncno ambiente.

VAR='rsync -avz --{partial,stats,delete,exclude=".*"}'

O formulário entre aspas não funciona porque chaves não são expandidas entre aspas, e não dentro de atribuições, nem são expandidas depois que uma variável é expandida.

Se você precisar armazenar argumentos de linha de comando em uma variável, use uma matriz:

args=(rsync -avz --{partial,stats,delete,exclude=".*"})

Agora "${args[@]}"vai se expandir para rsync, -avz, --partial, etc. como palavras distintas.

As matrizes também permitem anexar opções à lista, condicionalmente, se necessário, para que você possa, por exemplo:

args=(this that)
if something ; then
    args+=(another_arg)
fi
"$cmd" "${args[@]}"
ilkkachu
fonte
1

Você pode pelo menos salvar as opções parcialmente em uma variável:

opts=$(echo --{ignore-case,word-regexp,count,exclude='"sys*.*"'})

O teste é importante, porque o mascaramento pode ser difícil:

echo $opts
--ignore-case --word-regexp --count --exclude="sys*"

grep $opts bytes *.log 

Como existem várias alternativas, como usar o histórico, usar um alias, usar uma função, não há nenhum caso de uso óbvio em que eu possa pensar. Raramente existe um compartilhamento de opções complexo entre programas diferentes; portanto, para uma solução ad-hoc para o shell interativo, o alias parece uma maneira melhor:

alias cgrep='grep --ignore-case --word-regexp --count --exclude="sys*"'
cgrep bytes *.log

Sua amostra

VAR=rsync -avz --{partial,stats,delete,exclude=".*"}

não pode funcionar, porque a atribuição é finalizada no primeiro espaço em branco. Você tem que mascarar os espaços em branco:

VAR='rsync -avz --{partial,stats,delete,exclude=".*"}'

uma coisa bastante perigosa para testar, com essa opção --delete, não é? Como as opções podem conter novamente "," e aspas simples, o mascaramento pode ficar difícil muito em breve. Eu iria procurar um apelido ou confiar na história.

Um alias pode ser armazenado no arquivo ~ / .bashrc para uso contínuo em várias sessões. As funções também podem ser armazenadas no bashrc, mas você só precisa delas, se quiser manipular parâmetros, passadas para a função a ser avaliada nela.

Usuário desconhecido
fonte