作业控制:你以为你不需要的 Bash 功能
世界上的人基本上分为三种:对 bash 作业控制知之甚少或一无所知的人,了解一些但认为自己永远不会使用它的人,以及可以略读这篇文章其余部分的人。现在,别误会我的意思,我并不是说 bash 的作业控制会改变你的世界,但是有几个简单的日常场景,作业控制可能很有用,而且通常,它甚至可以消除“糟糕”的时刻。
那么,什么是作业,什么是作业控制? 你从 bash 提示符运行的任何脚本或程序都是一个作业。 如果脚本或程序启动了其他进程(例如,bash 脚本中的管道),则这些进程与主脚本或程序属于同一作业。 作业控制允许您挂起作业,将作业从后台移动到前台,反之亦然,从前台移动到后台。 使用 script & 运行脚本会创建一个后台作业。 仅使用 script 运行脚本会创建一个前台作业。
作业控制由以下命令组成
- 以下fg命令将后台作业移动到前台。
- 以下bg命令将挂起的前台作业移动到后台。
- 以下jobs命令显示当前作业列表。
- 以下kill命令可以终止作业或向其发送信号。
- 以下disown命令从作业列表中删除作业(不终止它)。
- 可以通过键入 ^Z (Control-Z) 来挂起前台作业。 挂起的作业会暂时停止。
直到最近,对我来说,所有这些都只是让人联想到大型机操作员和大型系统管理员; 似乎对我没什么用处。 但事实证明,作业控制实际上可以帮助你处理日常事务。
例如,假设您正在使用 vi 编辑内容,并且您决定要查看另一个文件。 您可以通过挂起您的第一个 vi 会话并启动另一个会话来完成此操作
$ vi somefile
^Z
[1]+ Stopped vi somefile
$ vi otherfile
请注意,挂起 vi 将导致其“窗口”消失,而不是 bash 窗口,只是 vi 的“全屏”输出。
如果您想暂时返回到第一个文件,您可以挂起第二个 vi 会话,并将第一个会话带回前台,这将恢复第一个会话的 vi 窗口
^Z
[2]+ Stopped vi otherfile
$ jobs
[1]- Stopped vi somefile
[2]+ Stopped vi otherfile
# Bring fist vi session to the foreground.
$ fg 1
我知道所有 vi 用户都在尖叫,说只要拆分窗口并在同一 vi 会话中打开两个文件即可。 但是,除非您是 vi 高级用户,否则这意味着您首先必须上网,搜索如何在 vi 中拆分窗口,然后分心阅读有关特朗普最新推文的内容,好吧,情况可能会每况愈下……
当然,您也可以只打开另一个 shell 提示符,并在新 shell 中运行新的 vi 副本,或任何您想运行的程序。 有时这很有意义,但有时作业控制只是更快更简单。
挂起程序并在前台和后台之间移动程序也适用于您从命令行启动的 GUI 应用程序。 此功能主要用于将 GUI 程序移出前台。 例如,通常在命令行工作时,我想打开一个 GUI 编辑器或电子表格,以便在 shell 提示符的同时查看某些内容。 我没有使用桌面菜单来启动程序,而只是在命令行输入其名称,例如,kate用于 KDE GUI 编辑器
$ ls
# ... bash stuff
# Now open a file with kate, the KDE GUI editor
$ kate somefile
这就是其中一个“糟糕”的时刻,因为我真正想做的是输入kate somefile &在后台启动 kate 而不占用我的终端。 现在我必须退出 kate 并使用以下命令重新运行它&或者我必须打开另一个终端才能继续我在 shell 中正在做的事情(并在此过程中丢失对当前 bash 历史记录的访问权限)。 但是使用作业控制,我无需做任何这些事情。 我只是挂起 kate(它在前台)并将其移动到后台
^Z
[3]+ Stopped kate
$ bg 3
[3]+ kate &
$ # and now the bash prompt returns
当您挂起 GUI 应用程序时,它的窗口不会消失,但是如果您尝试在其中键入内容或使用鼠标,您会发现它没有响应。
您可能已经注意到“fg”和“bg”命令的数字参数。 这些是您想要在前台和后台之间移动的作业编号。 当您使用jobs命令时,方括号中的数字是作业编号
$ jobs
[1]+ Stopped vi somefile
[2]+ Stopped vi otherfile
[3]+ Running kate &
通常您甚至不需要指定作业编号,因为您想要的作业通常是您刚刚挂起的作业或您刚刚重新启动的作业,而 bash 将此称为“当前作业”。 Bash 还有其他引用作业的方式; 请参阅 bash 手册页以获取更多信息(在手册页中搜索“^job”以转到相关部分)。
回到“kate”示例,由于我不太可能再次将 GUI 程序移回前台,因此我可以“disown”它并将其从作业列表中删除
$ jobs
[1]+ Stopped vi somefile
[2]+ Stopped vi otherfile
[3]+ Running kate &
$ disown 3
$ jobs
[1]+ Stopped vi somefile
[2]+ Stopped vi otherfile
$
到目前为止,我一直在将前台内容移动到后台或恢复挂起的任务,但是您可能还想将您在后台启动的任务移动到前台。 现在,当您只想键入“script”时,您不太可能意外键入“script &”,但是如果您在后台运行程序并且它需要来自终端的输入,则可能会出现这种需求(如果后台脚本尝试从终端读取,则会自动挂起)。 考虑以下将系统日志转储到文件的脚本
#!/bin/bash
sudo journalctl >log
提示:阻止后台作业写入终端。
默认情况下,后台作业可以写入终端。 如果您曾经在后台启动某些内容并且其输出意外地出现在您的终端窗口中,您可能已经看到过这种情况。 您可以像读取一样,通过发出以下命令使后台任务在写入时自动挂起stty tostop命令。
如果您不想等待它完成,您可以在后台运行它。 但是,启动它后,您很可能会发现它已被挂起
$ bash dumplog.sh &
$ jobs
[1]+ Stopped bash dumplog.sh
那是因为脚本要做的第一件事是执行“sudo”,而 sudo 需要提示您输入密码(假设它没有缓存您的密码)。 作业控制再次发挥作用。 您只需将脚本带回前台,输入密码,挂起作业,然后将其移回后台
$ bash dumplog.sh &
$ jobs
[1]+ Stopped bash dumplog.sh
$ fg 1
[sudo] password for root: ******
^Z
$ bg 1
$
最后一点说明,最新版本的 bash(我的版本是 5.0.7)或可能是最新版本的内核(我的版本是 5.1.10)似乎破坏了作业控制。 在测试上面的“dumplog”示例时,在将作业从前台移动到后台,然后再两次移回前台后,Control-Z(和 Control-C)停止工作。 它在其他示例中工作正常,在较旧的系统上也可以正常工作。
---
我在文章中找到的任何并非来自其他来源的代码,都应被视为按以下方式许可
# Copyright 2019 Mitch Frazier <mitch -at- linuxjournal.com>
#
# This software may be used and distributed according to the terms of the
# MIT License or the GNU General Public License version 2 (or any later version).