如何将shell命令行参数转义并将其存储到一个参数中?

时间:2019-03-15 00:03:25

标签: shell docker

任何人说这是一个坏主意的Inb4,对于这样的事情实际上是一种合理的方法。

我正在编写这个Docker容器,该容器可以通过Docker中包含的OpenVPN连接执行用户提供的命令,例如docker run vpntunnel curl example.com

因此,映像的ENTRYPOINT将启动OpenVPN,在VPN隧道启动后,执行用户提供的CMD行。

问题是,OpenVPN启动后运行命令的标准方法是通过OpenVPN的--up选项。这是此选项的手册页描述:

--up cmd
  Run command cmd after successful TUN/TAP device open (pre --user UID change).
  cmd consists of a path to script (or executable program), optionally followed
  by arguments. The path and arguments may be single- or double-quoted and/or
  escaped using a backslash, and should be separated by one or more spaces.

因此,这里的合理方法是让ENTRYPOINT脚本正确转义用户提供的CMD行,并将整个内容作为一个参数传递给OpenVPN的--up选项。

如果我的Docker映像需要在隧道启动后且在执行用户命令行之前执行一些初始化,则可以在用户提供CMD行之前,像这样--up 'tunnel-up.sh CMD...'和最后一个脚本中添加脚本tunnel-up.sh行使用"$@"执行用户提供的参数。

现在,您可能会猜到,剩下的唯一问题是如何正确地转义整个命令行以能够作为单个参数传递。

幼稚的方法只是--up "tunnel-up.sh $@",但它肯定无法区分a b c"a b" c之间的命令行。

1 个答案:

答案 0 :(得分:1)

在bash 4.4+中,您可以使用带有@的参数转换来引用值:

--up "tunnel-up.sh ${*@Q}"

在以前的版本中,您可以使用printf '%q'来达到相同的效果:

--up "tunnel-up.sh $((($#)) && printf '%q ' "$@")"

(($#))检查确保在调用printf之前有要打印的参数。)