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


29

我想运行这样的事情:

bash -c "some_program with its arguments"

但是要让交互式的bash在some_program结束后继续运行。

我敢肯定,-c这不是一个好方法man bash

交互式shell是一个启动时启动的,没有非选项参数,也没有-c选项

那么该怎么做呢?

这里描述主要目标

注意

  • 我需要some_program不时终止
  • 我不想把它放在后台
  • 我想继续bash做其他事情
  • 我希望能够再次运行该程序

1
如果目标是如此复杂,您也应该在这里进行解释。但这只是一个建议
Kiwy 2014年

1
我试图在这里尽可能准确地描述简短。但是我没想到大多数人不会专注于细节,而是会尝试提出其他建议。我会做一些注释以使其清楚。
pawel7318 2014年

在另一个问题中,候机楼的目的是什么?您描述的目标是可行的,但需要处理I / O。您的平均subshel​​l不会通过常规文件轻松处理终端转义的I / O。您应该研究pty.
mikeserv

顺便说一句,它在我下面的回答中起作用的唯一原因是因为我偷了终端。但是,最终,父进程可能会收回它-然后您正在查看4kb缓冲区。
mikeserv 2014年

您为什么不想在后台放置程序?在后台启动它,做一些bash,然后与fg
Bernhard

Answers:


8
( exec sh -i 3<<SCRIPT 4<&0 <&3                                        
    echo "do this thing"
    echo "do that thing"
  exec  3>&- <&4
  SCRIPT
)

最好使用脚本来完成此操作,exec $0.或者使用或如果这些文件描述符之一定向到当前未使用的终端设备,它将有所帮助-您必须记住,其他进程也要检查该终端。

顺便说一句,如果您的目标是执行脚本后保留脚本的环境(我假设是这样),则可能会更好:

. ./script

外壳程序.dotbash's source不是相同的-外壳程序.dot是POSIX指定为内置的特殊外壳程序,因此几乎可以保证,尽管这绝不能保证它会存在...

虽然以上应该可以按照您的期望做,但问题不大。例如,您可以:

 ( 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更多,并且,正如另一个答案所示trapkill还有另外两个。

如果尚未收到有关并发运行的后台进程状态的即时通知,那是因为您当前的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。我曾经读过一篇很有启发性的文章,将该过程比作道格拉斯·亚当斯(Douglas Adams)对行星NowWhat的描述

“在《银河系漫游指南》中,道格拉斯·亚当斯提到了一个极其沉闷的星球,其中居住着一群沮丧的人类和某些带有锋利牙齿的动物,它们通过在大腿上用力地咬人与人类进行交流。这是惊人的。与UNIX类似,在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详细信息。


看起来很有趣。今天晚些时候我将能够对其进行测试。
pawel7318 2014年

23

这是一个简短的解决方案,可以完成您想要的操作,但是除非您了解问题和bash的工作原理,否则可能没有任何意义:

bash -i <<< 'some_program with its arguments; exec </dev/tty'

这将启动bash shell some_program并启动start ,并在some_program退出后将您放到bash shell中。

基本上,我们要做的是在STDIN上输入bash字符串。那串是some_program with its arguments; exec </dev/tty。这告诉bash首先启动some_program,然后再运行exec </dev/tty。因此,bash不再继续从传递的字符串中读取命令,而是从读取/dev/tty

-i是因为bash启动时会检查STDIN是否为tty,启动时不是。但是稍后会出现,因此我们将其强制为交互模式。


另一种解决方案

我认为这是非常可移植的另一个想法是将以下内容添加到~/.bashrc文件的末尾。

if [[ -n "START_COMMAND" ]]; then
  start_command="$START_COMMAND"
  unset START_COMMAND
  eval "$start_command"
fi

然后,当您要首先使用命令启动外壳程序时,请执行以下操作:

START_COMMAND='some_program with its arguments' bash

说明:

其中大多数应该是显而易见的,但是对于变量名更改的原因是,我们可以对变量进行本地化。由于$START_COMMAND是导出的变量,它将被外壳的任何子代继承,并且如果另一个bash shell是这些子代中的一个,它将再次运行命令。因此,我们将该值分配给新的未导出变量($start_command),然后删除旧变量。


如果不止一个炸毁了什么?一个命令?不,仍然可以工作。但是您对可移植性是正确的。有2个因素,<<<不是POSIX,但是您可以echo "string here" | bash -i选择。然后就是/dev/ttyLinux。但是您可以在启动bash之前先将FD进行复制,然后重新打开STDIN,该外观与您正在执行的操作相似,但我选择保持简单。
Patrick

好吧好吧,别打架。这对我有用,并做了我需要的一切。我不太在乎POSIX和可移植性,我只在盒子上需要它。我还将检查mikeserv的答案,但现在无法执行此操作。
pawel7318 2014年

1
不打架:-)。我尊重mikserv的回答。他追求兼容性,我追求简单。两者都是完全有效的。如果兼容性不太复杂,有时我会寻求兼容性。在这种情况下,我认为这不值得。
Patrick

2
@Patrick,/dev/tty不是Linux,而是POSIX。
jlliagre 2014年

2
嘿,whatdayaknow
帕特里克

8

这应该可以解决问题:

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”选项

  • 我希望能够再次运行该程序

选择“重新启动”选项


不错不错..但是能够返回到最后一条命令也很好。有什么想法吗?请在此处检查我的另一个问题以了解我需要什么。所以也许您会建议另一种方法
pawel7318

这将执行一个子shell。我认为询问者正在尝试保留脚本环境。
mikeserv 2014年

谢谢jlliagre-不错的尝试,但对我来说没有多大用处。我通常按​​ctrl + C,我希望它能做它应该做的事。附加菜单就太多了。
pawel7318 2014年

@ pawel7318的行为CTRL+C只是终端默认配置(将其解释为)的副作用SIGINT。您可以使用进行修改stty
mikeserv

@ pawel7318进一步说明,SIGINTtrapped上方2
mikeserv

3

您可以通过将脚本作为初始化文件进行传递来实现:

bash --init-file foo.script

或者,您可以在命令行上传递它:

bash --init-file <(echo "ls -al")

注意,这--init-file是为了读取系统范围的初始化文件,/etc/bash.bashrc因此您可能需要source在脚本中“ ” 读取这些文件。


0

我真的看不出这样做的意义,因为您已经在运行该程序后返回了shell。但是,您可以这样做:

bash -c "some_program with its arguments; bash"

程序运行后,这将启动交互式bash。


这将启动一个子外壳。
mikeserv

0

您可以将命令放在后台以保持当前bash处于打开状态:

some_program with its arguments &

要返回运行状态,则可以使用 fg命令^+z并将其再次置于后台


这次不行。您假设我想从bash运行此bash ...情况并非如此。
pawel7318 2014年

@ pawel7318如果您再说明一点,我们还能为您提供更好的答案吗?
2014年

在这里检查我的另一个问题。
pawel7318 2014年

@ pawel7318如果您的问题相关,请在您自己的问题中将链接添加到其他问题。
2014年

1
@ pawel7318 bash与否,如果我无法处理进程背景,则您正在使用一个非常陌生的外壳。我同意猕猴桃-如果您有疑问,该信息将为您提供更好的服务。
mikeserv 2014年

0

您可能要使用屏幕来运行命令。然后,您可以在命令完成后重新连接到会话。

或者,只需在后台运行命令some_program with its arguments&。一旦完成,这将使您能够重新运行命令并获得命令的状态。


我正好从中运行它,screen但是将它放到后台对我没有用-我有时需要终止程序,执行其他操作然后再次运行。主要目标是快速完成此任务。
pawel7318 2014年

@ pawel7318您可以使用kill %或命令终止后台程序kill %1
BillThor 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.