我想运行这样的事情:
bash -c "some_program with its arguments"
但要让交互式 bash 在some_program
结束后继续运行。
我确信-c
这不是一个好方法man bash
:
交互式 shell 是在没有非选项参数且没有 -c 选项的情况下启动的
那么如何做到这一点呢?
主要目标已描述这里
笔记
- 我需要
some_program
时不时地终止 - 我不想把它放到后台
- 我想留下
bash
来做点别的事 - 我希望能够再次运行该程序
答案1
这是一个较短的解决方案,可以完成您想要的任务,但除非您了解问题以及 bash 的工作原理,否则可能没有意义:
bash -i <<< 'some_program with its arguments; exec </dev/tty'
这将启动 bash shell start some_program
,退出后some_program
,您将进入交互式 shell。
基本上我们正在做的就是在 bash 的 STDIN 上输入一个字符串。该字符串告诉 bash 启动some_program
,然后运行exec </dev/tty
。告诉exec </dev/tty
bash 将 STDIN 从我们给它的字符串切换过来/dev/tty
,这使得它变得交互式。
这-i
是因为当 bash 启动时,它会检查 STDIN 是否是 tty,而当 bash 启动时则不是。但以后会的,所以我们强制 bash 进入交互模式。
另一种解决方案
我认为非常便携的另一个想法是将以下内容添加到文件的最后~/.bashrc
。
if [[ -n "START_COMMAND" ]]; then
start_command="$START_COMMAND"
unset START_COMMAND
eval "$start_command"
fi
然后,当您想首先使用命令启动 shell 时,只需执行以下操作:
START_COMMAND='some_program with its arguments' bash
解释:
其中大部分应该是显而易见的,但是变量名称更改的原因是我们可以本地化变量。由于$START_COMMAND
是导出变量,因此它将被 shell 的任何子级继承,如果另一个 bash shell 是这些子级之一,它将再次运行该命令。因此,我们将该值分配给一个新的未导出变量 ( $start_command
) 并删除旧变量。
答案2
( exec sh -i 3<<SCRIPT 4<&0 <&3 ⏎
echo "do this thing"
echo "do that thing"
exec 3>&- <&4
SCRIPT
)
这最好通过脚本来完成,exec $0.
或者如果这些文件描述符之一指向当前未使用的终端设备,它将有所帮助 - 你必须记住,其他进程也想检查该终端。
顺便说一句,如果您的目标是,正如我所假设的那样,在执行脚本后保留脚本的环境,那么您可能会得到更好的服务:
. ./script
shell 的.dot
和bash's source
不是一个相同的 - shell.dot
是 POSIX 指定为特殊 shell 内置的,因此尽可能接近得到保证,尽管这绝不保证它会在那里......
尽管上面的内容应该符合您的预期,没有什么问题。例如,您可以:
( exec sh -i 3<<SCRIPT 4<&0 <&3 ⏎
echo "do this thing"
echo "do that thing"
$(cat /path/to/script)
exec 3>&- <&4
SCRIPT
)
shell 将运行您的脚本并将您返回到交互式提示符 - 只要您避免exit
从脚本中调用 shell,也就是说,或者将您的进程置于后台 - 这会将您的 i/o 链接到/dev/null.
演示:
% printf 'echo "%s"\n' "These lines will print out as echo" \
"statements run from my interactive shell." \
"This will occur before I'm given the prompt." >|/tmp/script
% ( exec sh -i 3<<SCRIPT 4<&0 <&3
echo "do this thing"
echo "do that thing"
$(cat /tmp/script)
exec 3>&- <&4
SCRIPT
)
sh-4.3$ echo "do this thing"
do this thing
sh-4.3$ echo "do that thing"
do that thing
sh-4.3$ echo "These lines will print out as echo"
These lines will print out as echo
sh-4.3$ echo "statements run from my interactive shell."
statements run from my interactive shell.
sh-4.3$ echo "This will occur before I'm given the prompt."
This will occur before I'm given the prompt.
sh-4.3$ exec 3>&- <&4
sh-4.3$
许多JOBS
我认为您应该更熟悉 shell 的内置任务管理选项。 @Kiwy 和 @jillagre 都已经在他们的答案中谈到了这一点,但可能需要进一步的细节。我已经提到了一个 POSIX 指定的内置特殊 shell,但是set, jobs, fg,
和bg
还有更多,并且,正如另一个答案所示,trap
还有kill
还有两个。
如果您尚未收到有关并发运行的后台进程状态的即时通知,这是因为您当前的 shell 选项设置为 POSIX 指定的默认值-m
,但您可以使用以下set -b
方式异步获取这些通知:
% man set
−b This option shall be supported if the implementation supports the User Portability Utilities option. It shall cause the shell to notify the user asynchronously of background job completions. The following message is written to standard error:
"[%d]%c %s%s\n", <job-number>, <current>, <status>, <job-name>
where the fields shall be as follows:
<current> The character '+' identifies the job that would be
used as a default for the fg or bg utilities; this
job can also be specified using the job_id "%+" or
"%%". The character '−' identifies the job that
would become the default if the current default job
were to exit; this job can also be specified using
the job_id "%−". For other jobs, this field is a
<space>. At most one job can be identified with '+'
and at most one job can be identified with '−'. If
there is any suspended job, then the current job
shall be a suspended job. If there are at least two
suspended jobs, then the previous job also shall be a
−m This option shall be supported if the implementation supports the User Portability Utilities option. All jobs shall be run in their own process groups. Immediately before the shell issues a prompt after completion of the background job, a message reporting the exit status of the background job shall be written to standard error. If a foreground job stops, the shell shall write a message to standard error to that effect, formatted as described by the jobs utility. In addition, if a job changes status other than exiting (for example, if it stops for input or output or is stopped by a SIGSTOP signal), the shell shall write a similar message immediately prior to writing the next prompt. This option is enabled by default for interactive shells.
基于 Unix 的系统的一个非常基本的特征是它们处理进程的方法signals
。我曾经读过一篇启发性文章将该过程比作道格拉斯·亚当斯对地球的描述的主题怎么办:
“在《银河系漫游指南》中,道格拉斯·亚当斯提到了一个极其暗淡的星球,居住着一群抑郁的人类和某种具有锋利牙齿的动物,它们通过用力咬住人类的大腿来与人类交流。这令人震惊类似于 UNIX,其中内核通过向进程发送瘫痪或致命信号来与进程进行通信,进程可能会拦截某些信号,并尝试适应这种情况,但大多数进程不会。”
这是指kill signals
.
% kill -l
> HUP INT QUIT ILL TRAP ABRT BUS FPE KILL USR1 SEGV USR2 PIPE ALRM TERM STKFLT CHLD CONT STOP TSTP TTIN TTOU URG XCPU XFSZ VTALRM PROF WINCH POLL PWR SYS
至少对我来说,上面的引文回答了很多问题。例如,我一直认为如果我想监视一个dd
进程,我就必须kill
这样做,这很奇怪,而且一点也不直观。读完后觉得有道理。
我想说的是他们中的大多数不要尝试去适应有充分的理由 - 让一堆进程向您的终端发送开发人员认为对您可能重要的任何信息进行垃圾邮件发送,这可能会带来更大的烦恼,而不是带来的好处。
取决于您的终端配置(您可以检查stty -a
),CTRL+Z
可能设置为将 a 转发SIGTSTP
到当前的前台进程组领导者,这可能是您的 shell,并且默认情况下也应该将其配置trap
为该信号并挂起您的最后一个命令。同样,正如 @jillagre 和 @Kiwy 的答案一起显示的那样,您可以根据自己的喜好定制此功能以适应您的目的。
SCREEN JOBS
因此,要利用这些功能,您首先需要了解它们并根据自己的需要自定义它们的处理方式。例如,我刚刚发现Github 上的这个 screenrc其中包括screen
以下键绑定SIGTSTP
:
# hitting 'C-z C-z' will run Ctrl+Z (SIGTSTP, suspend as usual)
bind ^Z stuff ^Z
# hitting 'C-z z' will suspend the screen client
bind z suspend
这将使挂起作为子进程运行的进程screen
或挂起screen
子进程本身变得很简单。
紧接着:
% fg
或者:
% bg
根据您的喜好将进程置于前台或后台。内置功能jobs
可以随时为您提供这些列表。添加-l
操作数将包括 pid 详细信息。
答案3
这应该可以解决问题:
bash -c "some_program with its arguments;bash"
编辑:
这是更新后的新尝试:
bash -c "
trap 'select wtd in bash restart exit; do [ \$wtd = restart ] && break || \$wtd ; done' 2
while true; do
some_program with its arguments
done
"
- 我需要时不时地终止 some_program
使用ControlC,您将看到这个小菜单:
1) bash
2) restart
3) exit
- 我不想把它放到后台
就是这样。
- 我想继续狂欢然后做点别的事
选择“bash”选项
- 我希望能够再次运行该程序
选择“重新启动”选项
答案4
$ bash --init-file <(echo 'some_program with its arguments')
$ bash --rcfile <(echo 'some_program with its arguments')
如果您无法使用进程替换:
$ cat script
some_program with its arguments
$ bash --init-file script
和sh
(dash
,busybox
):
$ ENV=script sh
或者:
$ bash -c 'some_program with its arguments; exec bash'
$ sh -c 'some_program with its arguments; exec sh'