Enrole funções bash em uma função runner

1

Meu script contém muitos mysqldump blabla > dump.sql e mysq balbla < dump.sql em para tornar possível executá-lo no modo dry-run .

Na verdade, o objetivo é criar uma funtion run para executar qualquer coisa que eu peça.

  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: couldn't find table: ">"

    
por smarber 28.03.2018 / 09:42

3 respostas

5

Você deve usar "${@}" em vez de ${@} (como com echo "${@}" ), mas essa não é a razão do seu problema.

O motivo é 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 colocar > na linha de comando, o shell não está mais procurando > .

Um ponto importante que notei depois de publicar minha resposta: com uma chamada como

run mysqldump blabla > dump.sql

a função run não vê > e dump.sql . Isso provavelmente não é o que você deseja porque impede que você altere todos os redirecionamentos com uma única variável de ambiente, já que a saída de echo "${@}" é redirecionada para o arquivo também. Assim, você deve usar algo como run --redirect dump.sql mysqldump blabla , veja abaixo.

Existem duas possibilidades:

  1. Cole com "$@" e use eval . Isso pode levá-lo a um pesadelo de citação, é claro. Você precisa citar tudo, exceto o > , para que o shell veja um > na linha de comando antes de fazer a remoção da cotação.

  2. Manipule o redirecionamento separadamente:

    run --redirect dump.sql mysqldump blabla
    
    run() {
        if [ "$1" == '--redirect' ]; then
            shift
            redirect_target="$1"
            shift
        else
            redirect_target='/dev/stdout' # works at least with Linux
        fi
    
        if [[ "$(printenv DRY_RUN)" = "yes" ]]
        then
            echo "${@}"
        else
            "${@}" > "$redirect_target"
        fi
    }
    

    Você pode evitar o redirect_target='/dev/stdout' se você colocar o if [ "$1" == '--redirect' ] na filial else de if [[ "$(printenv DRY_RUN)" = "yes" ]] .

        if [[ "$(printenv DRY_RUN)" = "yes" ]]
        then
            if [ "$1" == '--redirect' ]; then
                # do not output "--redirect file"
                shift
                shift
            fi
            echo "${@}"
        else
            if [ "$1" == '--redirect' ]; then
                shift
                redirect_target="$1"
                shift
                "${@}" > "$redirect_target"
            else
                "${@}"
            fi
        fi
    
por 28.03.2018 / 10:21
0

Como em outra resposta recente que publiquei , 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 no post vinculado, ou usando bash -c "$*" , enquanto ainda precisa citar toda a linha de comando, incluindo redirecionamentos.

Embora se você não quiser citar os comandos, uma opção é definir -nv ( noexec e verbose ), de modo que o bash não execute os comandos, mas apenas os imprima. Então, em vez de um wrapper run , no início do script, faça:

[[ $DRY_RUN = yes ]] && set -nv
    
por 28.03.2018 / 10:22
-1

Use "$@" (com aspas duplas):

run() {
    if [[ "$(printenv DRY_RUN)" = "yes" ]]
    then
        echo "${@}"
    else
    "$@"
    fi
}

Sem eles, $@ se expande para um único token.

    
por 28.03.2018 / 10:18

Tags