我正在尝试写一个外壳脚本接收作为输入 a带参数的命令并运行它。
作为一个例子,我希望按cron
如下方式使用它:
0 11 * * * my_wrapper.sh "task_name" "command arg1 arg2 arg3 ..."
细节my_wrapper.sh
并不重要,但它是一个zsh
脚本,我希望它接收command arg1 arg2 arg3 ...
并调用它。请注意,参数可能包含单引号、双引号等。
向脚本传递和接收带有参数的命令的正确方法是什么?
更新:
在 zsh 的命令行上,@Gilles 的第一个解决方案效果很好:
#!/bin/zsh
task_name=$1
shift
"$@" > /path/to/logging_directory/$task_name
然后> my_wrapper.sh date "%Y-%b-%d"
从命令行调用即可完成这项工作。
但是,当我尝试按如下方式使用它时cron
:
CRON_WRAPPER="/long/path/to/my_wrapper.sh"
0 11 * * * $CRON_WRAPPER "current_date.log" date "+%Y-%b-%d"
这不起作用。
最终更新(问题已解决):
正如吉尔斯的回答所解释的那样,crontab
需要逃跑任何%
迹象。将上述内容更改为:
CRON_WRAPPER="/long/path/to/my_wrapper.sh"
0 11 * * * $CRON_WRAPPER "current_date.log" date "+\%Y-\%b-\%d"
有效。可以了,好了。
答案1
您有两种选择:您可以传递一个带有一些参数的程序来执行,或者您可以传递一个 shell 脚本。这两个概念都可以称为“命令”。
A带有一些参数的程序采用字符串列表的形式,其中第一个是可执行文件的路径(或不包含任何斜杠的名称,可在环境变量指示的目录列表中查找PATH
)。这样做的优点是用户可以将参数传递给该命令,而不必担心引用;用户可以显式调用 shell(sh -c …
如果他们愿意)。如果选择此选项,请将每个字符串(程序及其参数)作为单独的参数传递给脚本。这些通常是脚本的最后一个参数(如果您希望能够传递更多参数,则需要指定一个特殊字符串作为程序结束参数标记,然后您就无法将其传递给程序除非你使语法变得更加复杂)。
0 11 * * * my_wrapper.sh "task_name" command arg1 arg2 arg3 ...
在你的脚本中:
#!/bin/zsh
task_name=$1
shift
"$@"
A外壳脚本sh
是传递给程序执行的字符串。这允许用户编写任何 shell 片段,而无需显式调用 shell,并且更易于解析,因为它是单个字符串,因此是程序的单个参数。在sh
脚本中,您可以调用eval
,但不要在 zsh 中执行此操作,因为用户不会期望必须编写与 sh 语法略有不同的 zsh 语法。
0 11 * * * my_wrapper.sh "task_name" "command arg1 arg2 arg3 ..."
在你的脚本中:
#!/bin/zsh
task_name=$1
sh -c "$2"
答案2
处理这个问题的最佳方法是将实际的命令参数作为参数而不是字符串传递给包装器。你可以这样调用你的包装器:
my_wrapper "task_name" command arg1 arg2 arg3
my_wrapper
将包含以下内容:
task_name=$1
shift # remove the task_name from the command+args
"$@" # execute the command with args, while preserving spaces etc
答案3
我认为破折号选项也有类似的问题,我在搜索中遇到了这个问题,所以我想分享一下对我有帮助的内容。我正在使用以下 crontab:
0 23 * * * sudo -u myname /home/myname/bin/buildme.sh -f >> /home/myname/log.txt
在 bash 脚本中,我使用它来获取 -f 选项:
while getopts ":f" opt; do
case $opt in
f)
force_full=1
;;
\?)
echo "Invalid option: -$OPTARG" >&2
;;
esac
done
因此,我注意到,当我由于某种原因通过 cron 运行此选项时,该选项并未得到执行。好吧,将 /bin/bash 添加到 cronjob 就可以解决这个问题。新的 crontab 是:
0 23 * * * sudo -u myname /bin/bash /home/myname/bin/buildme.sh -f >> /home/myname/log.txt