首页 > 解决方案 > 在 bash 脚本中执行 `sh -c`

问题描述

我有一个test.sh将bash命令作为参数的文件,它执行一些逻辑,即设置和检查一些环境变量,然后执行该输入命令。

#!/bin/bash

#Some other logic here

echo "Run command: $@"
eval "$@"

当我运行它时,这是输出

% ./test.sh echo "ok"        
Run command: echo ok
ok

但问题是,当我通过类似的东西时sh -c 'echo "ok"',我没有得到输出。

% ./test.sh sh -c 'echo "ok"'
Run command: sh -c echo "ok"

%

所以我尝试用 更改evalexec尝试$@直接执行(不使用evalor exec),甚至尝试执行它并将输出保存到变量中,仍然没有用。

有没有办法以这种格式运行传递的命令并获取我们的输入?


用例:该脚本用作 docker 容器的入口点,它从 docker CMD 接收参数并执行这些参数以运行容器。

作为快速修复,我可以删除sh -c并在没有它的情况下传递命令,但我想让脚本可重用而不是更改命令。

标签: linuxbashshell

解决方案


TL;博士:

这是一个典型的用例(在运行复合命令之前在 Docker 入口点脚本中执行一些业务逻辑,在命令行中给出),推荐的脚本最后一行是:

exec "$@"

细节

为了进一步解释这一行,一些评论和超链接:

  1. 根据Bash 用户手册exec它是一个 POSIX shell 内置,它在不创建新进程的情况下替换 shell [使用提供的命令]

  2. 因此,exec在 Docker 入口点上下文中使用这样的方法很重要,因为它确保CMD执行的程序仍然具有 PID 1 并且可以直接处理信号,包括docker stop(另请参见其他 SO 答案:加速 docker-compose关机)。

  3. 双引号 ( ) 对于避免分词"$@"也很重要(即确保每个位置参数按原样传递,即使它包含空格)。参见例如:

     #!/usr/bin/env bash
    
     printargs () { for arg; do echo "$arg"; done; }
    
     test0 () {
         echo "test0:"
         printargs $@
     }
    
     test1 () {
         echo "test1:"
         printargs "$@"
     }
    
     test0 /bin/sh -c 'echo "ok"'
     echo
     test1 /bin/sh -c 'echo "ok"'
    
    test0:
    /bin/sh
    -c
    echo
    "ok"
    
    test1:
    /bin/sh
    -c
    echo "ok"
    
  4. 最后eval是一个强大的 bash 内置函数,它 (1) 您的用例不需要,(2) 实际上不建议在一般情况下使用,特别是出于安全原因。例如,如果字符串参数eval依赖于一些用户提供的输入......有关此问题的详细信息,请参阅例如https://mywiki.wooledge.org/BashFAQ/048(它回顾了一些想要使用此内置函数的情况,通常是命令eval "$(ssh-agent -s)")。


推荐阅读