Meu script bash contém muitos mysqldump blabla > dump.sql
e mysq balbla < dump.sql
para possibilitar a execução no modo de simulação .
Na verdade, o objetivo é criar uma função run
para executar qualquer coisa que eu pedir.
run echo 'hello world'
run mysqldump blabla > dump.sql
run mysql blabla < dump.sql
run ssh blabla
# etc
run() {
if [[ "$(printenv DRY_RUN)" = "yes" ]]
then
echo "${@}"
else
${@}
fi
}
No entanto, isso não funciona:
run "mysqldump -uuser -ppass dbase > dump.sql"
Eu recebo este erro:
mysqldump: não foi possível encontrar a tabela: ">"
Você deve usar
"${@}"
em vez de${@}
(como comecho "${@}"
), mas esse não é o motivo do seu problema.A razão é que o redirecionamento ocorre muito cedo na análise da linha de comando, antes da substituição do parâmetro. Assim, depois que a variável foi colocada
>
na linha de comando, o shell não está procurando>
mais.Um ponto importante que notei depois de publicar minha resposta: Com uma chamada como
a função
run
não vê>
edump.sql
. Isso provavelmente não é o que você deseja, porque impede que você altere todos os redirecionamentos com uma única variável de ambiente, pois a saídaecho "${@}"
também é redirecionada para o arquivo. Assim, você deve usar algo comorun --redirect dump.sql mysqldump blabla
, veja abaixo.Existem duas possibilidades:
Fique com
"$@"
e useeval
. Isso pode levá-lo a um pesadelo de citação, é claro. Você tem que citar tudo, exceto para>
que o shell veja um vazio>
na linha de comando antes de fazer a remoção das citações.Trate o redirecionamento separadamente:
Você pode evitar o
redirect_target='/dev/stdout'
se colocar oif [ "$1" == '--redirect' ]
noelse
ramo deif [[ "$(printenv DRY_RUN)" = "yes" ]]
.Como em outra resposta recente que postei , a divisão de palavras acontece tarde demais para iniciar o redirecionamento (mas não tarde demais para afetar o redirecionamento).
Você pode executar o comando usando
eval
, como sugerido em outra resposta na postagem vinculada, ou usandobash -c "$*"
, enquanto ainda precisa citar toda a linha de comando, incluindo redirecionamentos.Porém, se você quiser evitar ter que citar os comandos, uma opção é definir
-nv
(noexec
everbose
), para que o bash não execute os comandos, mas apenas os imprima. Portanto, em vez de um wrapperrun
, no início do script, faça:Use
"$@"
(com aspas duplas):Sem eles,
$@
expande para um único token.