命令执行后如何保持Bash运行?

命令执行后如何保持Bash运行?

我想运行这样的事情:

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/ttybash 将 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 的.dotbash'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 -aCTRL+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

shdashbusybox):

$ ENV=script sh

或者:

$ bash -c 'some_program with its arguments; exec bash'
$ sh -c 'some_program with its arguments; exec sh'

相关内容