在 Bash 中,如何以编程方式获取以 开头的作业的作业 ID &
?
可以在后台启动一个作业&
,然后使用其作业 ID 和 Bash 内置命令(如fg
、bg
、kill
等)与其进行交互。
例如,如果我开始像这样的工作
yes > /dev/null &
然后我可以使用以下命令杀死它(假设该作业获取作业 ID 1):
kill %1
使用 创建新作业时&
,如何以编程方式获取新创建作业的作业 ID?
我知道你可以获得进程号(不是职位 ID)$!
,但我特别想知道如何获得职位 ID。
答案1
该命令jobs
打印当前正在运行的后台作业及其 ID:
$ for i in {1..3}; do yes > /dev/null & done
[1] 3472564
[2] 3472565
[3] 3472566
$ jobs
[1] Running yes > /dev/null &
[2]- Running yes > /dev/null &
[3]+ Running yes > /dev/null &
因此,要获取最后启动的仍在运行的作业的 ID,因为它将被标记为+
,你可以这样做(使用 GNU grep
):
$ jobs | grep -oP '\d(?=]\+)'
3
或者,更便携:
$ jobs | sed -n 's/^\[\([0-9]*\)\]+.*/\1/p'
但是,请注意,如果您暂停其中一项作业,那么该作业将会占用+
.所以你可能只想取最后一行:
$ jobs | tail -n1 | cut -d' ' -f1 | tr -d '][+'
3
答案2
引用自 bash 手册:
有多种方法可以在 shell 中引用作业。字符 % 引入了工作规范 (jobspec)。作业编号 n 可以称为 %n。还可以使用用于启动作业的名称的前缀或使用出现在其命令行中的子字符串来引用作业。例如,%ce 指的是已停止的 ce 作业。如果一个前缀匹配多个作业,bash 会报告错误。另一方面,使用 %?ce 指的是在其命令行中包含字符串 ce 的任何作业。如果子字符串匹配多个作业,bash 会报告错误。 符号 %% 和 %+ 指的是 shell 的当前作业概念,即最后一个在前台停止或在后台启动的作业。 可以使用 %- 引用前一个作业。如果只有一个作业,%+ 和 %- 都可以用来指代该作业。在与作业相关的输出中(例如,jobs 命令的输出),当前作业始终用 + 标记,前一个作业用 - 标记。 单个%(没有随附的工作规范)也指当前工作。
此外,该命令还jobs -l
列出了作业 ID 和 PID,这可能允许您$!
使用某些脚本将 PID(从 获得)转换为作业 ID。
答案3
作业编号可以重复使用,进程 ID 和进程组 ID 可以重复使用,不能保证在调用cmd &
和调用之间job
,正在运行的进程cmd
不会终止。
在 中bash
, 的输出jobs
通常无论如何都不可进行后处理。
您可以信赖的是,之后
A | B | C &
如果您没有trap
异步启动管道,$!
则将包含已开始运行的进程的 pid C
。
运行A
、B
及其C
后代(如果有)的进程将在一项作业中(在启用作业控制的交互式 shell 中)。 shell 将选择第一个空闲作业号。仅当没有其他挂起的作业时,该作业才会成为当前作业(在/ /的参数中也称为%+
(或%%
什至在某些 shell 中))。%
kill
fg
bg
jobs -l
在 bash 中列出了作业以及它所知道的其中进程的 ID。似乎它甚至列出了已经终止的那些(列出了在我的测试中运行的看起来true
像一个错误的),所以你应该能够在那里查找。但要可靠地做到这一点是不可能的。Running
sleep 100 | true
$!
zsh
通过使用它的特殊关联数组,你会更幸运,$jobstates
它的键是作业 id 和值,例如running:+:37632=running:37633=done
job_id=${(k)jobstates[(R)*:$!=*]}
此处查找值包含 的元素:$!=
,请记住,虽然不太可能,但两个作业具有相同 pid 的进程并非不可能,因为在进程终止且其父进程已确认其死亡后,pid 可以重用。
现在,一旦你有了这个job_id
,你必须确保在使用它之前没有其他作业在后台启动或暂停,因为 job_id 很可能已被重用。
您可以在作业名称中插入唯一的字符串并通过 引用它们,而不是依赖作业 ID %?that-string
。
例如:
sleep 100 | sleep 101 $(: first) &
sleep 123 | sleep 123 $(: second) &
kill '%?first'
会在进程运行时终止作业sleep 100
,sleep 101
如果仍在运行,并且在您运行时两个进程都已死亡kill
,您将收到kill: %?first: no such job
错误。