我偶然发现了以下bash
行为,这对我来说有点出乎意料。
# The following works
$ declare bar=Hello # Line 1
$ declare -p bar # Line 2
declare -- bar="Hello"
$ foo=bar # Line 3
$ declare ${foo}=Bye # Line 4
$ declare -p bar # Line 5
declare -- bar="Bye"
# The following fails, though
$ declare -a array=( A B C ) # Line 6
$ declare -p array # Line 7
declare -a array=([0]="A" [1]="B" [2]="C")
$ foo=array # Line 8
$ declare -a …
Run Code Online (Sandbox Code Playgroud) 使用简单的ANSI 转义序列应该可以保存和恢复光标位置
ANSI 转义序列允许您在屏幕上随意移动光标。这对于 shell 脚本生成的全屏用户界面更有用,但也可以在提示中使用。运动转义序列如下:
- [...]
- 保存光标位置:
\033[s
- 恢复光标位置:
\033[u
然而,这个 ANSI 序列似乎只恢复光标的水平位置。例如:
$ printf 'Doing some task...\e[s\n\nMore text\n\e[udone!\n\n\n'
Doing some task...
More text
done!
$
Run Code Online (Sandbox Code Playgroud)
其中done!
水平位于正确位置,但垂直方向不正确(在恢复的意义上是正确的)。
done!
在之后打印task...
?我搜索并阅读了许多问题,但我没有找到任何关于我经历过的这种行为的信息。tput
实际上, via也会发生同样的情况
$ printf 'Doing some task...'; tput sc; printf '\n\nMore text\n'; tput rc; printf 'done!\n\n\n'
Run Code Online (Sandbox Code Playgroud) 免责声明:这个问题出现的时间比预期的要长得多。我把它分成 5 个子问题。在打开之前,我真的很想弄清楚自己的想法,但是此刻太多的方面让我感到困惑。
为了阐明我对如何以可靠的方式正确处理 Bash 中的进程的想法,我偶然发现了这篇 Greg 的 Wiki 文章。在那里,而不是在开始时,有这样的声明
如果您仍处于启动您想要对其进行处理的子进程的父进程中,那就太完美了。您可以保证 PID 是您的子进程(死的或活的),原因如下所述。您可以使用
kill
它来发出信号、终止它,或者只是检查它是否仍在运行。您可以使用wait
等待它结束或在它结束时获取其退出代码。
在页面的末尾,找到了下面解释的上述原因。
每个 UNIX 进程也有一个父进程。此父进程是启动它的进程,但
init
如果父进程在新进程结束之前结束,则可以更改为该进程。(也就是说,init
将选择孤立进程。)理解这种父/子关系至关重要,因为它是 UNIX 中可靠进程管理的关键。进程wait
终止后,进程的 PID 将永远不会被释放以供使用,直到父进程为 PID 查看它是否结束并检索其退出代码。如果父进程结束,进程将返回到init
,它会为您执行此操作。这是一个主要的原因,重要的是:如果父进程管理其子进程,也可以是绝对肯定的是,即使孩子过程中的模具,没有其他新的进程可能会意外地回收子进程的PID,直到父进程已经
wait
编为PID并注意到孩子死了。这让父进程保证它为子进程拥有的 PID 将始终指向该子进程,无论它是活着的还是“僵尸”。没有其他人有这种保证。不幸的是,这种保证不适用于 shell 脚本。Shell 积极地获取它们的子进程并将退出状态存储在内存中,在调用
wait
. 但是因为孩子在调用之前就已经被收割了wait
,所以没有僵尸来持有PID。内核可以自由地重用该 PID,而您的保证已被违反。
到目前为止,我已多次阅读以上段落,但我仍然不确定我是否正确理解了其背后的信息。
问题 1:从第二个长引用,特别是从它的最后一段,我会得出结论,在 shell(我只对 Bash 感兴趣)脚本中,我不能 100% 确定我存储在变量中的 PID 仍然指的是我启动了后台进程,因为它可能被内核重用于任何其他进程(甚至不是子进程)。这样对吗?上述保证适用于系统中的哪些地方? …