【问题标题】:Background process appears to hang后台进程似乎挂起
【发布时间】:2015-06-19 12:42:08
【问题描述】:

编者注:OP 最终希望从this answer 打包代码 作为脚本。所述代码创建了一个保持打开的 FIFO,后台命令从该 FIFO 读取数据以在数据到达时进行处理。

如果我在终端中键入它会起作用,但如果我在脚本文件中输入这些命令并运行它,它将不起作用。

 #!/bin/bash
 cat >a&
 pid=$!

程序好像卡在cat>a&

$pid运行脚本后没有任何值,但cat进程似乎存在。

【问题讨论】:

  • 你想捉什么猫? 'a' 将为空。
  • 当然pid 在脚本退出后 不会有值——所有的shell 变量,就其本质而言,当shell 退出时就会消失。脚本退出 -> 运行脚本的 shell 退出 -> 不再有变量。
  • @Prabhu 我正在尝试为问题的答案编写脚本。仅当我在终端中逐行输入时才有效serverfault.com/questions/443297/…
  • 这是哪个版本的 bash?如果您真正想要的是一个协同进程(根据链接,它看起来很可能),那么现代(4.x)版本的 bash 实现这种效果的方式要少得多。
  • @CharlesDuffy:假设我对 OP 正在尝试做的事情是正确的(请参阅我的回答):我看不到使用 coproc 的方法,但我很好奇如果你能想到一个。

标签: bash shell background-process jobs fifo


【解决方案1】:

你需要用“cat”指定一个文件:

 #!/bin/bash
 cat SOMEFILE >a &
 pid=$!
 echo PID $pid

虽然这看起来有点傻 - 为什么不直接“cp”文件 (cp SOMEFILE a)?

问:你到底想完成什么?

【讨论】:

  • 优点是可以用cat连接多个文件,但不能用cp连接多个文件。只是说。
  • 哦,另一件你不能用cp 做的事情:你可以用cat 字符设备,例如/dev/null。 (只是说明显而易见的。)
  • 按照 OP 的链接——a 是一个 FIFO,这使得不使用 cp 的原因变得清晰。
【解决方案2】:

cat >a“挂起”的原因是因为它正在从标准输入流(stdin,文件描述符零)读取,默认为键盘。

添加& 会使其在后台运行,从而断开与键盘的连接。通常这会将暂停的作业留在后台,但是,由于您退出脚本,其后台任务将被终止(发送 SIGHUP 信号)。

编辑:虽然我关注了问题中的链接,但最初并没有说明 OP 在那个阶段实际上正在使用 FIFO。感谢@mklement0。

我不明白您在这里要做什么,但我怀疑您需要将其作为“来源”文件运行,如下所示:

. gash.sh

gash.sh 是您的脚本名称。注意前面的.

【讨论】:

  • 我认为您对source 脚本的需求一针见血。但是,我怀疑“挂起”不是来自cat 命令(OP 状态是使用& 甚至交互地执行),而是来自随后尝试写入 FIFO @ 987654328@,由于脚本已退出(我从链接的 serverfault.com 问题中填写了缺失的部分),在其另一端没有人再听了。
  • @mklement0:绝对。
【解决方案3】:

cdarke's answer 包含 关键指针您的脚本不能在 子进程 中运行,因此您必须来源

根据您链接到的问题,听起来您正在尝试执行以下操作:

  • 打开一个 FIFO(命名管道)。
  • 让 FIFO 无限期打开。
  • 每当有新数据发送到它时,就从该 FIFO 中读取 background 命令。

查看底部的可行解决方案。

关于你的症状的解释

  • 运行您的脚本NOT sourced(NOT with .)意味着该脚本在一个子进程中运行,它具有以下内容含义:

    • 脚本中定义的变量仅在该脚本内可见,并且当脚本完成运行时变量将完全不存在。
      • 这就是您在运行脚本后没有看到脚本的$myPid 变量的原因。
    • 当脚本完成运行时,它的后台任务 (cat >a&) 被终止(正如 cdarke 解释的那样,SIGHUP 信号被发送给它们;任何没有明确捕获该信号的进程都会被终止)。
      • 这与您声称cat 进程继续存在的说法相矛盾,但我的猜测是您将以交互方式 启动的cat 进程误认为是由脚本启动的进程.
  • 相比之下,由您的脚本创建的任何 FIFO(使用 mkfifo确实在脚本退出后仍然存在(FIFO 的行为类似于文件 - 它一直存在到您明确删除它)。

    • 但是,当您写入到该 FIFO 而没有其他进程读取时,写入命令将阻塞,因此 出现挂起写入进程阻塞,直到另一个进程读取来自FIFO的数据)​​。
      • 这可能就是您的情况:因为脚本的后台进程被杀死,没有人从 FIFO 中读取,导致尝试写入它被阻塞。您错误地推测“卡住”的是 cat >a& 命令。

以下脚本在获取源代码后会向当前 shell 添加函数,用于使用后台命令设置和清理保持打开的 FIFO,该命令在数据到达时进行处理。保存为文件bgfifo_funcs:

#!/usr/bin/env bash

[[ $0 != "$BASH_SOURCE" ]] || { echo "ERROR: This script must be SOURCED." >&2; exit 2; }

# Set up a background FIFO with a command listening for input.
# E.g.:
#    bgfifo_setup bgfifo "sed 's/^/@ /'"
#    echo 'hi' > bgfifo # -> '@ hi'
#    bgfifo_cleanup
bgfifo_setup() {

  (( $# == 2 )) || { echo "ERROR: usage: bgfifo_setup <fifo-file> <command>" >&2; return 2; }

  local fifoFile=$1 cmd=$2

  # Create the FIFO file.
  mkfifo "$fifoFile" || return

  # Use a dummy background command that keeps the FIFO *open*.
  # Without this, it would be closed after the first time you write to it.
  # NOTE: This call inevitably outputs a job control message that looks
  #       something like this:
  #         [1]+  Stopped                 cat > ...
  { cat > "$fifoFile" & } 2>/dev/null

  # Note: The keep-the-FIFO-open `cat` PID is the only one we need to save for
  #       later cleanup.
  #       The background processing command launched below will terminate
  #       automatically then FIFO is closed when the `cat` process is killed.
  __bgfifo_pid=$!

  # Now launch the actual background command that should read from the FIFO 
  # whenever data is sent.
  { eval "$cmd" < "$fifoFile" & } 2>/dev/null || return

  # Save the *full* path of the FIFO file in a global variable for reliable
  # cleanup later.
  __bgfifo_file=$fifoFile
  [[ $__bgfifo_file == /* ]] || __bgfifo_file="$PWD/$__bgfifo_file"

  echo "FIFO '$fifoFile' set up, awaiting input for: $cmd"
  echo "(Ignore the '[1]+  Stopped ...' message below.)"
}

# Cleanup function that you must call when done, to remove
# the FIFO file and kill the background commands.
bgfifo_cleanup() {
  [[ -n $__bgfifo_file ]] || { echo "(Nothing to clean up.)"; return 0; }
  echo "Removing FIFO '$__bgfifo_file' and terminating associated background processes..."
  rm "$__bgfifo_file"
  kill $__bgfifo_pid  # Note: We let the job control messages display.
  unset __bgfifo_file __bgfifo_pid
  return 0
}

然后,source 脚本bgfifo_funcs,使用. shell 内置:

. bgfifo_funcs

Sourcing 在当前 shell中执行脚本(而不是在脚本运行后终止的子进程中),从而使脚本的函数和变量可用到当前的外壳。 根据定义,函数在当前 shell 中运行,因此从函数启动的任何后台命令都保持活动状态

现在您可以使用后台进程设置保持打开的 FIFO,该进程在输入到达时处理输入,如下所示:

# Set up FIFO 'bgfifo in the current dir. and process lines sent to it
# with a sample Sed command that simply prepends '@ ' to every line.
$ bgfifo_setup bgfifo "sed 's/^/@ /'"

# Send sample data to the FIFO.
$ echo 'Hi.' > bgfifo
@ Hi.

# ...

$ echo 'Hi again.' > bgfifo
@ Hi again.

# ...

# Clean up when done.
$ bgfifo_cleanup

【讨论】:

    猜你喜欢
    • 2014-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-13
    • 2016-05-15
    相关资源
    最近更新 更多