Concatenação de string bash usada para criar lista de parâmetros

12

Dado este pedaço de bash:

PARMS='-rvu'
PARMS+=" --delete --exclude='.git'"
echo $PARMS
rsync ${PARMS} . ${TARGET}

O eco mostra a sequência de PARMS conforme o esperado, nenhum erro é exibido, mas o rsync age silenciosamente como se as opções adicionadas pelo + = não existissem. No entanto, isso funciona conforme o esperado:

PARMS='-rvu'
rsync ${PARMS} --delete --exclude='.git' . ${TARGET}

Eu acho que estraguei algo com aspas bash (sempre tive problemas com elas), mas não sei exatamente o que e por que as opções são ignoradas, mesmo que a string pareça ter sido criada corretamente.

neuviemeporte
fonte
1
echo "$PARMS"e rsync "${PARMS}"...
jasonwryan 28/08
Isso funciona para mim com a bashversão 4.2.25 sem nenhuma alteração.
Anthon

Respostas:

17

Há uma diferença entre:

PARMS+="... --exclude='.git'"

e

... --exclude='.git'

Na primeira, as aspas simples estão entre aspas, e estão literalmente presentes no texto substituído, dado rsynccomo argumento. rsyncobtém um argumento cujo valor é --exclude='.git'. No segundo, as aspas simples são interpretadas pelo shell no momento em que são escritas, porque elas não estão entre aspas e rsyncconseguem ver --exclude=.git.

Nesse caso, você não precisa de aspas simples - .gité uma palavra shell perfeitamente válida por si só, sem caracteres especiais, para que você possa usá-la literalmente no comando.

Melhor para esse tipo de coisa, porém, é uma matriz :

PARMS=(-rvu)
PARMS+=(--delete --exclude='.git')
rsync "${PARMS[@]}"

Isso cria seu comando como palavras separadas, com qualquer citação que você deseja interpretar no momento em que escreve a linha da matriz. "${PARMS[@]}"expande para cada entrada na matriz como um argumento separado, mesmo que o argumento em si tenha caracteres ou espaços especiais, para rsyncque ele veja o que você escreveu como pretendia.

Michael Homer
fonte
bashexecutou a divisão da palavra após ter ${PARMS}sido expandida. Portanto, as aspas simples também foram interpretadas pelo shell.
cuonglm
2
Tente! Eu fiz. As aspas permanecem e, se houver espaços entre eles, eles são pontos de divisão de qualquer maneira.
22714 Michael Homer
@Gnouc: A partir da página man bash: "Citar Remoção: Após as expansões anteriores, todas as ocorrências não cotadas dos personagens \ , 'e ". Que não resultou de uma das expansões acima são removidas" "expansões acima" inclui expansão de parâmetro que realiza a expansão de ${PARMS}.
CAMH
Obrigado. Portanto, entendo que, nesse caso, omitir as aspas simples entre as duplas funcionará, mas por uma questão de exaustividade - e se eu precisasse citar alguns caracteres especiais e não quisesse usar sua última abordagem?
Neuviemeporte 28/08
Se seus caracteres especiais não fazem parte IFS(geralmente de espaço em branco), você não precisa citá-los. Se estiverem, você não terá sorte, a menos que hackear algo junto eval- isso é um pouco incorreto em geral, e as matrizes são a maneira certa de lidar com isso.
28814 Michael Homer
2

Além da resposta de @Michael Homer , você pode usar a bash função eval :

PARMS='-rvu'
PARMS+=" --delete --exclude='.git'"
echo "$PARMS"
eval "rsync ${PARMS} . "'"${TARGET}"'
cuonglm
fonte
2
Você pode, mas não deveria. Matrizes foram adicionadas especificamente para evitar esse uso de eval.
Chepner 29/08/14