Passando um comando com argumentos para um script

6

Estou tentando escrever um script de shell que recebe como entrada um comando com argumentos e o executa.

Como exemplo, espero usar isso em cron da seguinte forma:

0 11 * * * my_wrapper.sh "task_name" "command arg1 arg2 arg3 ..."

Os detalhes de qual my_wrapper.sh não importam, mas é um script zsh e eu quero receber command arg1 arg2 arg3 ... e invocá-lo. Note que os argumentos podem conter aspas simples, aspas duplas, etc.

Qual é a maneira correta de passar e receber comandos com argumentos para scripts?

Atualização:

Na linha de comando em zsh, a primeira solução da @Gilles funciona muito bem:

#!/bin/zsh
task_name=$1
shift
"$@" > /path/to/logging_directory/$task_name

e, em seguida, invocar > my_wrapper.sh date "%Y-%b-%d" da linha de comando faz o trabalho.

No entanto, quando tento usá-lo da seguinte forma em cron :

CRON_WRAPPER="/long/path/to/my_wrapper.sh"
0 11 * * * $CRON_WRAPPER "current_date.log" date "+%Y-%b-%d"

Não funciona.

Atualização final (problema resolvido):

Como explicado na resposta de Gilles, crontab exige que escape qualquer sinal % . Depois de alterar o acima para:

CRON_WRAPPER="/long/path/to/my_wrapper.sh"
0 11 * * * $CRON_WRAPPER "current_date.log" date "+\%Y-\%b-\%d"

funcionou. Tudo pronto.

    
por Amelio Vazquez-Reina 28.12.2014 / 06:24

3 respostas

3

Você tem duas opções: pode passar um programa para executar com alguns argumentos ou pode passar um script de shell. Ambos os conceitos podem ser chamados de "um comando".

Um programa com alguns argumentos assume a forma de uma lista de strings, a primeira das quais é o caminho para um arquivo executável (ou um nome que não contenha nenhuma barra, para ser consultado no lista de diretórios indicados pela variável de ambiente PATH ). Isso tem a vantagem de que o usuário pode passar argumentos para esse comando sem se preocupar em citar; o usuário pode invocar um shell explicitamente ( sh -c … se quiser). Se você escolher isto, passe cada string (o programa e seu argumento) como um argumento separado para o seu script. Estes seriam tipicamente os últimos argumentos para o seu script (se você quer ser capaz de passar mais argumentos, você precisa designar uma string especial como um marcador de argumentos de fim de programa, o qual você não pode passar para o programa a menos que você torne a sintaxe ainda mais complicada).

0 11 * * * my_wrapper.sh "task_name" command arg1 arg2 arg3 ...

e no seu script:

#!/bin/zsh
task_name=$1
shift
"$@"

Um script de shell é uma string que você passa para o programa sh para execução. Isso permite ao usuário escrever qualquer fragmento de shell sem ter que invocar explicitamente um shell, e é mais simples de analisar, já que é uma única string, portanto, um único argumento para o seu programa. Em um script sh , você poderia chamar eval , mas não faça isso em zsh, porque os usuários não esperariam ter que escrever a sintaxe zsh que é um pouco diferente da sintaxe sh.

0 11 * * * my_wrapper.sh "task_name" "command arg1 arg2 arg3 ..."

e no seu script:

#!/bin/zsh
task_name=$1
sh -c "$2"
    
por 29.12.2014 / 00:29
4

A melhor maneira de lidar com isso é passar o comando real args para seu wrapper como args ao invés de uma string. Você pode chamar seu wrapper assim:

my_wrapper "task_name" command arg1 arg2 arg3

my_wrapper conteria o seguinte:

task_name=$1
shift # remove the task_name from the command+args
"$@" # execute the command with args, while preserving spaces etc
    
por 28.12.2014 / 06:56
1

Eu estava tendo um problema semelhante com as opções de traço e me deparei com essa pergunta aqui na minha pesquisa, então queria compartilhar o que me ajudou. Eu estava usando o seguinte crontab:

0 23 * * * sudo -u myname /home/myname/bin/buildme.sh -f >> /home/myname/log.txt

E dentro do script bash eu estava usando isso para obter a opção -f:

while getopts ":f" opt; do
    case $opt in
        f)
            force_full=1
            ;;
        \?)
            echo "Invalid option: -$OPTARG" >&2
            ;;
    esac
done

Então eu notei que a opção não estava sendo honrada quando eu fiz isso através do cron por algum motivo. Bem, adicionando / bin / bash ao cronjob, consertamos. O novo crontab é:

0 23 * * * sudo -u myname /bin/bash /home/myname/bin/buildme.sh -f >> /home/myname/log.txt
    
por 07.10.2018 / 15:58

Tags