【问题标题】:executing shell command in background from script [duplicate]从脚本在后台执行shell命令[重复]
【发布时间】:2010-09-10 10:36:11
【问题描述】:

如果命令在字符串中,我如何在 bash 脚本中在后台执行 shell 命令?

例如:

#!/bin/bash
cmd="nohup mycommand";
other_cmd="nohup othercommand";

"$cmd &";
"$othercmd &";

这不起作用——我该怎么做?

【问题讨论】:

标签: bash unix shell


【解决方案1】:

去掉引号

$cmd &
$othercmd &

例如:

nicholas@nick-win7 /tmp
$ cat test
#!/bin/bash

cmd="ls -la"

$cmd &


nicholas@nick-win7 /tmp
$ ./test

nicholas@nick-win7 /tmp
$ total 6
drwxrwxrwt+ 1 nicholas root    0 2010-09-10 20:44 .
drwxr-xr-x+ 1 nicholas root 4096 2010-09-10 14:40 ..
-rwxrwxrwx  1 nicholas None   35 2010-09-10 20:44 test
-rwxr-xr-x  1 nicholas None   41 2010-09-10 20:43 test~

【讨论】:

  • 谢谢。据我所知,有必要省略分号。
  • 还要确保$cmd 周围加上引号。如果你这样做了,它将尝试运行一个名为 "ls -la" 的命令,而不是使用开关 -lals
  • cmd='some-command "with arguments"'; $cmd根本some-command "with arguments"不同。如果您在参数列表中引用,它将失败;如果您需要控制 glob 何时扩展和不扩展,它将失败;如果在执行时运行依赖于大括号扩展或参数扩展的代码,它将失败。请参阅BashFAQ #50我正在尝试将命令放入变量中,但复杂的情况总是失败!.
【解决方案2】:

基于ngoozeff 的回答,如果您想让命令完全在后台运行(即,如果您想隐藏其输出并且当你关闭它的终端窗口时防止它被杀死),你可以这样做:

cmd="google-chrome";
"${cmd}" &>/dev/null & disown;
  • &>/dev/null 将命令的stdoutstderr 设置为/dev/null,而不是从父进程继承它们。
  • & 使 shell 在后台运行命令。
  • disown 从 shell 的作业控制下删除“当前”作业,最后一个已停止或置于后台。

在某些 shell 中,您还可以使用 &! 代替 & disown;它们都具有相同的效果。不过,Bash 不支持 &!

另外,将命令放入变量时,使用eval "${cmd}" 比使用"${cmd}" 更合适:

cmd="google-chrome";
eval "${cmd}" &>/dev/null & disown;

如果您直接在终端中运行此命令,它将显示该命令启动的进程的 PID。但是在 shell 脚本中没有输出会显示出来。

这是它的一个函数:

#!/bin/bash

# Run a command in the background.
_evalBg() {
    eval "$@" &>/dev/null & disown;
}

cmd="google-chrome";
_evalBg "${cmd}";

另请参阅:Running bash commands in the background properly

【讨论】:

  • @user248237dfsf @GreenRaccoon23 eval "${cmd}" 肯定比${cmd} 好得多。我相信这就是您的意思,因为"${cmd}" 将在以下情况下失败:cmd='ls -l'。此外,${cmd} 本身并不是完美的解决方案,因为在参数扩展之前使用扩展的情况下它会失败。例如cmd='touch file{1..5}'.
  • eval "$@" 是错误的——如果有人希望 "$@" 确保参数彼此分开传递,使用 eval 会失败(通过将所有参数连接到一个字符串中,然后对其进行评估)。最好要么使用"$@"(期望参数被预先拆分)或eval "$1"(期望只有一个参数被格式化用于解析)。
  • @CharlesDuffy eval "$@" 出问题的例子是什么?我想不出一个。我可以想到"$@" 会出错的情况,比如间接变量。正如您所指出的,eval "${1}" 也会忽略多个参数。 eval "$@" 将处理这两种边缘情况。我确实同意eval "${1}" 应该足够了,因为您的代码在调用该函数的方式上应该是一致的。从长远来看,在 _evalBg "${cmd}" 中使用引号而不是 _evalBg ${cmd} 将使代码更易于管理。
  • @GreenRaccoon23,作为一个示范案例,考虑set -- printf '%s\n' "first argument" "second argument" -- "$@" 会自己工作,eval "$@" 不会。
  • @GreenRaccoon23, ...是的,我将忽略多个参数定位为一个特性,而不是一个错误,理由是根本不支持一个案例比严重支持它更好: 要么接受带有要运行的代码的单个字符串 (eval "$1"),要么接受参数列表 ("$@");但eval "$@" 的行为方式与eval "$*" 完全相同,但存在所有隐含的错误。
【解决方案3】:

这是有效的,因为它是一个静态变量。 你可以做一些更酷的事情:

filename="filename"
extension="txt"
for i in {1..20}; do
    eval "filename${i}=${filename}${i}.${extension}"
    touch filename${i}
    echo "this rox" > filename${i}
done

此代码将创建 20 个文件并动态设置 20 个变量。当然,您可以使用数组,但我只是向您展示该功能:)。请注意,您可以使用变量 $filename1、$filename2、$filename3...,因为它们是使用评估命令创建的。在这种情况下,我只是创建文件,但您可以使用它为命令创建动态参数,然后在后台执行。

【讨论】:

    【解决方案4】:

    例如,您有一个名为 run.sh 的启动程序以在后台启动它,请执行以下命令行。 ./run.sh &>/dev/null &

    【讨论】: