我想运行这样的东西:
bash -c "some_program with its arguments"
Run Code Online (Sandbox Code Playgroud)
但要让交互式 bash 在some_program结束后继续运行。
我敢肯定-c这不是man bashseys的好方法:
交互式 shell 是一个没有非选项参数和 -c 选项的启动
那么如何做到这一点呢?
笔记
some_program不时终止bash来做别的事phe*_*mer 29
这是一个较短的解决方案,可以完成您想要的,但除非您了解问题以及 bash 的工作原理,否则可能没有意义:
bash -i <<< 'some_program with its arguments; exec </dev/tty'
Run Code Online (Sandbox Code Playgroud)
这将启动一个 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
Run Code Online (Sandbox Code Playgroud)
然后,当您想先使用命令启动 shell 时,只需执行以下操作:
START_COMMAND='some_program with its arguments' bash
Run Code Online (Sandbox Code Playgroud)
大部分应该是显而易见的,但是改变变量名的原因是我们可以对变量进行本地化。由于$START_COMMAND是一个导出变量,它将被 shell 的任何子级继承,如果另一个 bash shell 是这些子级之一,它将再次运行该命令。因此,我们将该值分配给一个新的未导出变量 ( $start_command) 并删除旧变量。
mik*_*erv 12
( exec sh -i 3<<SCRIPT 4<&0 <&3 ?
echo "do this thing"
echo "do that thing"
exec 3>&- <&4
SCRIPT
)
Run Code Online (Sandbox Code Playgroud)
这最好通过脚本完成,但exec $0.如果这些文件描述符之一指向当前未使用的终端设备,它将有所帮助 - 您必须记住,其他进程也想检查该终端。
顺便说一句,如果您的目标是,正如我所假设的那样,在执行脚本后保留脚本的环境,您可能会更好地使用:
. ./script
Run Code Online (Sandbox Code Playgroud)
shell 的.dotandbash's source不是一个和相同的 - shell 的.dotPOSIX 被指定为一个特殊的 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
)
Run Code Online (Sandbox Code Playgroud)
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$
Run Code Online (Sandbox Code Playgroud)
JOBS我认为您应该更熟悉 shell 的内置任务管理选项。@Kiwy 和 @jillagre 都已经在他们的回答中提到了这一点,但它可能需要进一步的细节。而且我已经提到内置一个POSIX指定的特殊外壳,但set, jobs, fg,与bg更多的是几个,并且,作为另一个答案演示trap和kill两个更多的还是。
如果您还没有收到有关并发运行后台进程状态的即时通知,那是因为您当前的 shell 选项设置为 POSIX 指定的默认值-m,但您可以使用以下方法异步获取这些通知set -b:
% man set
Run Code Online (Sandbox Code Playgroud)
Run Code Online (Sandbox Code Playgroud)?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
Run Code Online (Sandbox Code Playgroud)
Run Code Online (Sandbox Code Playgroud)?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 的系统的一个非常基本的特征是它们处理 process 的方法signals。我曾经读过一篇关于这个主题的启发性文章,将这个过程比作道格拉斯·亚当斯对NowWhat星球的描述:
“在《银河系漫游指南》中,道格拉斯·亚当斯提到了一个极其沉闷的星球,居住着一群抑郁的人类和某种具有锋利牙齿的动物,它们通过用力咬住人类的大腿来与人类交流。这是惊人的类似于 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
Run Code Online (Sandbox Code Playgroud)
至少对我来说,上面的引用回答了很多问题。例如,我一直认为如果我想监控一个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
Run Code Online (Sandbox Code Playgroud)
这将使暂停作为子screen进程或screen子进程本身运行的进程成为一件简单的事情。
紧接着:
% fg
Run Code Online (Sandbox Code Playgroud)
或者:
% bg
Run Code Online (Sandbox Code Playgroud)
将根据您的喜好将过程前景或背景。该jobs内置可随时为你提供这些列表。添加-l操作数将包括 pid 详细信息。
这应该可以解决问题:
bash -c "some_program with its arguments;bash"
Run Code Online (Sandbox Code Playgroud)
编辑:
这是更新后的新尝试:
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
"
Run Code Online (Sandbox Code Playgroud)
- 我需要不时终止 some_program
使用ControlC,您将看到这个小菜单:
1) bash
2) restart
3) exit
Run Code Online (Sandbox Code Playgroud)
- 我不想把它放在背景中
就是这样。
- 我想留在狂欢然后做其他事情
选择“bash”选项
- 我希望能够再次运行该程序
选择“重启”选项
您可以通过将脚本作为初始化文件传递来完成此操作:
bash --init-file foo.script
Run Code Online (Sandbox Code Playgroud)
或者您可以在命令行上传递它:
bash --init-file <(echo "ls -al")
Run Code Online (Sandbox Code Playgroud)
请注意,这--init-file是为了读取系统范围的初始化文件/etc/bash.bashrc,因此您可能希望source在脚本中“”这些文件。