使用初始命令运行交互式bash子shell,而无需立即返回(“ super”)shell


86

我想运行一个bash子shell,(1)运行一些命令,(2),然后保留在该subshel​​l中以按照我的意愿进行。我可以单独执行以下每个操作:

  1. 使用-c标志运行命令:

    $> bash -c "ls; pwd; <other commands...>"

    但是,执行命令后,它将立即返回“ super”外壳。我还可以运行一个交互式子shell:

  2. 开始新的bash过程:

    $> bash

    直到我明确地说出来,它才会退出子外壳……但是我无法运行任何初始命令。我找到的最接近的解决方案是:

    $> bash -c "ls; pwd; <other commands>; exec bash"

    它可以工作,但不是我想要的方式,因为它在一个子shell中运行给定命令,然后打开一个单独的命令进行交互。

我想单行执行此操作。退出子外壳后,我应该回到常规的“超级”外壳,而不会发生任何事件。一定有办法~~

NB:我没有问...

  1. 不问在哪里可以掌握bash手册页
  2. 不问如何从文件中读取初始化命令...我知道该怎么做,这不是我要寻找的解决方案
  3. 对使用tmux或gnu屏幕不感兴趣
  4. 不愿意为此提供背景信息。即,该问题是笼统的,并非出于任何特定目的
  5. 如果可能的话,我想避免使用变通办法来实现我想要的目的,但是要以一种“肮脏”的方式。我只想单行执行此操作。特别是我不想做这样的事情xterm -e 'ls'

我可以想象有一个Expect解决方案,但这几乎不是您想要的那种。哪种exec bash解决方案不适合您?
glenn jackman 2012年

@glennjackman抱歉,我对行话不熟悉。什么是“期望解决方案”?此外,该exec bash解决方案涉及两个单独的子外壳。我想要一个连续的子壳。
SABBATINI卢卡

3
的妙处exec在于,它取代了与第二,第一子shell,所以你只剩父低于1个壳。如果您的初始化命令设置了环境变量,则它们将存在于执行的shell中。
格伦·杰克曼


2
并与问题exec是,你失去了,这不是通过环境传承给子shell任何东西,如非出口变量,函数,别名,...
柯特J.桑普森

Answers:


77

这可以通过临时命名管道轻松完成:

bash --init-file <(echo "ls; pwd")

这个答案值得Lie Ryan发表。我发现这确实很有用,并且在注释中不太明显,因此我认为应该是它自己的答案。


9
这大概意味着$HOME/.bashrc虽然没有执行。它必须包含在临时命名管道中。
Hubro

9
为了澄清,是这样的:bash --init-file <(echo ". \"$HOME/.bashrc\"; ls; pwd")
Hubro

5
这很麻烦,但确实有效。我不敢相信bash不直接支持这一点。
Pat Niemeyer

2
@Gus,.source命令的同义词:ss64.com/bash/source.html
乔纳森·波特

1
有没有办法使其与用户切换配合使用,例如sudo bash --init-file <(echo "ls; pwd")sudo -iu username bash --init-file <(echo "ls; pwd")
jeremysprofile

10

您可以使用临时文件以环形方式执行此操作,尽管它需要两行:

echo "ls; pwd" > initfile
bash --init-file initfile

为了获得良好的效果,您可以通过将临时文件包括在内rm $BASH_SOURCE来使其自身删除。
Eduardo Ivanec'3

爱德华多,谢谢。这是一个很好的解决方案,但是......你说这个不能做,而不具有文件I / O摆弄。我宁愿将其保留为自包含命令,这有明显的原因,因为一旦文件混合在一起,我就不得不开始担心如何制作随机的临时文件,然后再删除它们,就像您提到的那样。如果我要严格的话,以这种方式只需要付出更多的努力。因此,需要一种更简约,更优雅的解决方案。
SABBATINI卢卡

1
@SABBATINILuca:我不是在说那样的话。这只是一种方法,mktemp确实可以解决@cjc指出的临时文件问题。Bash 可以支持从stdin读取init命令,但据我所知还不行。指定-为初始化文件并将其管道传输一半有效,但是Bash随后退出(可能是因为它检测到了管道)。恕我直言,优雅的解决方案是使用exec。
Eduardo Ivanec'3

1
这还不覆盖您的常规bash初始化吗?@SABBATINILuca您想以此实现什么,为什么需要生成一个shell自动运行一些命令然后保持该shell打开的状态?
user9517 2012年

11
这是一个古老的问题,但是Bash可以使用以下语法创建临时的命名管道:bash --init-file <(echo“ ls; pwd”)。
Lie Ryan

5

尝试以下方法:

$> bash -c "ls;pwd;other commands;$SHELL"

$SHELL 它使外壳程序以交互方式打开,并等待关闭exit


9
仅供参考,这将在之后打开一个新的shell,因此,如果任何命令影响当前shell的状态(例如,获取文件),则可能无法按预期运行
ThiefMaster

3

我指的“期望解决方案”是使用Expect编程语言编写bash shell :

#!/usr/bin/env expect
set init_commands [lindex $argv 0]
set bash_prompt {\$ $}              ;# adjust to suit your own prompt
spawn bash
expect -re $bash_prompt {send -- "$init_commands\r"}
interact
puts "exiting subshell"

您将像这样运行: ./subshell.exp "ls; pwd"


我想这也将具有在历史记录中注册命令的优势,我错了吗?另外我很好奇在这种情况下是否执行了bashrc / profile?
muhuk 2015年

确认这可以让您将命令放入历史记录,而其他解决方案则不允许。这对于在.screenrc文件中启动进程非常有用-如果退出已启动的进程,则不会关闭屏幕窗口。
丹·桑德伯格

1

为什么不使用本机子外壳?

$ ( ls; pwd; exec $BASH; )
bar     foo     howdy
/tmp/hello/
bash-4.4$ 
bash-4.4$ exit
$

用括号括起来的命令使bash生成一个运行这些命令的子进程,因此,例如,您可以更改环境而不影响父级shell。基本上,这等效于bash -c "ls; pwd; exec $BASH"

如果看起来仍然很冗长,则有两种选择。一种是将此代码片段作为功能:

$ run() { ( eval "$@"; exec $BASH; ) }
$ run 'ls; pwd;'
bar     foo     howdy
/tmp/hello/
bash-4.4$ exit
$ run 'ls;' 'pwd;'
bar     foo     howdy
/tmp/hello/
bash-4.4$ exit
$

另一个是exec $BASH缩短:

$ R() { exec $BASH; }
$ ( ls; pwd; R )
bar     foo     howdy
/tmp/hello/
bash-4.4$ exit
$

我个人R更喜欢这种方法,因为不需要使用转义字符串。


1
我不确定在OP中使用exec是否有任何麻烦,但是对我来说,这是提出的最好的解决方案,因为它使用普通的bash命令而没有任何字符串转义问题。
彼得

1

如果sudo -E bash不起作用,我将使用以下内容,到目前为止已达到我的期望:

sudo HOME=$HOME bash --rcfile $HOME/.bashrc

我设置HOME = $ HOME是因为我希望新会话将HOME设置为用户的HOME,而不是root的HOME,这在某些系统上默认发生。


0

不够优雅--init-file,但可能更具实用性:

fn(){
    echo 'hello from exported function'
}

while read -a commands
do
    eval ${commands[@]}
done
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.