扭矩 PBS 传递包含引号的环境变量

Torque PBS passing environment variables that contain quotes

我有一个 python 脚本。通常我会 运行 这样:

./make_graph data_directory "wonderful graph title"

我必须 运行 通过调度程序执行此脚本。我正在使用 -v 通过 qsub.

传递 python 脚本的参数
qsub make_graph.pbs -v ARGS="data_directory \"wonderful graph title\""

我尝试了很多 ', ", \" 转义的组合,但我就是做不好。 'wonderful graph title' 周围的引号总是丢失或损坏。

这是 pbs 脚本的摘录

if [ -z "${ARGS+xxx}" ]; then
        echo "NO ARGS SPECIFIED!"
        exit 1
fi

CMD="/path/make_graph $ARGS"
echo "CMD: $CMD"

echo "Job started on `hostname` at `date`"
${CMD}

将包含空格的字符串参数作为环境变量通过 qsub 传递的正确方法是什么?有一个更好的方法吗?也许这是一个更普遍的 bash 问题。

更新:此答案基于 SGE qsub 而不是 TORQUE qsub,因此 CLI 有所不同。特别是,TORQUE qub 似乎不支持直接参数传递,因此第二种方法不起作用。


这主要是正确引用的问题,与Grid Engine提交本身关系不大。如果你只是想修复你当前的脚本,你应该使用 eval "${CMD}" 而不是 ${CMD}。下面详细分析当你单独做 ${CMD} 时会发生什么(在分析中我们假设 path 没有什么好笑的):

  1. 您的 qsub 命令行已处理并删除了引号,因此传递的 ARGS 环境变量是 data_directory "wonderful graph title".

  2. 你做了CMD="/path/make_graph $ARGS",所以CMD的值是/path/make_graph data_directory "wonderful graph title"(我是在没有引号的情况下呈现字符串字面量,也就是字面值包含引号字符)。

  3. 你做到了 ${CMD}。 Bash 对此进行参数扩展,相当于:

    1. ${CMD} 扩展为其值 /path/make_graph data_directory "wonderful graph title"
    2. 由于没有引用${CMD},进行分词,所以最后命令行有五个词:/path/make_graphdata_directory"wonderfulgraphtitle"。最后四个被视为你的make_graph的参数,这当然不是你想要的。

另一方面,如果您使用 eval "${CMD}",那么就像您在交互式 shell 中键入 /path/make_graph data_directory "wonderful graph title" 一样,这是所需的行为。

您应该在 Bash Reference Manual.

中阅读有关 eval、参数扩展等的更多信息

更正后的脚本:

#!/usr/bin/env bash
[[ -z ${ARGS+xxx} ]] && { echo "NO ARGS SPECIFIED!" >&2; exit 1; }

CMD="/path/make_graph ${ARGS}"
echo "CMD: ${CMD}"

echo "Job started on $(hostname) at $(date)" # backticks are deprecated
eval "${CMD}"

顺便说一句,要测试它,您不需要将它提交给网格引擎;只是做

ARGS="data_directory \"wonderful graph title\"" bash make_graph.pbs

好的,我只是指出了错误并进行了修补。但它真的是 "proper way" 将参数传递给 Grid Engine 作业吗?不,我不这么认为。参数就是参数,不应与环境变量混淆。 qsub 允许您直接传递参数(qsub 概要:qsub [ options ] [ command | -- [ command_args ]]),那么为什么在环境变量中对它们进行编码并最终担心引用?

这是编写提交脚本的更好方法:

#!/usr/bin/env bash
[[ $# == 0 ]] && { echo "NO ARGS SPECIFIED!" >&2; exit 1; }

CMD="/path/make_graph $@"
echo "CMD: ${CMD}"

echo "Job started on $(hostname) at $(date)" # backticks are deprecated
/path/make_graph "$@"

此处 "$@" 等同于 "" "" ... — 忠实地按原样传递所有参数(请参阅 Bash 参考手册中的 relevant section)。

不幸的是,虽然执行的命令是正确的,但打印的命令可能没有正确引用。例如,如果你这样做

qsub make_graph.pbs data_directory "wonderful graph title"

那么执行的是make_graph.pbs data_directory "wonderful graph title",但是打印出来的CMDmake_graph.pbs data_directory wonderful graph title。据我所知,没有简单的方法来解决这个问题,因为无论如何进行分词,引号总是从参数中删除。如果打印的命令对你来说真的很重要,有两种解决方法:

  1. 使用专用的"shell escaper"(很容易自己写一个)在打印前引用参数;

  2. 使用另一种脚本语言,其中 shell 引用很容易获得,例如 Python (shlex.quote) 或 Ruby (Shellwords.shellescape).