如何调用 bash,在新 shell 中运行命令,然后将控制权交还给用户?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7120426/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 20:56:00  来源:igfitidea点击:

How to invoke bash, run commands inside the new shell, and then give control back to user?

shellbash

提问by Félix Saparelli

This must either be really simple or really complex, but I couldn't find anything about it... I am trying to open a new bash instance, then run a few commands inside it, and give the control back to the user inside that same instance.

这必须是非常简单或非常复杂的,但我无法找到任何关于它...我试图打开一个新的bash的实例,然后运行它里面一些命令,并给出了控制权交还给用户内部的同一个实例

I tried:

我试过:

$ bash -lic "some_command"

but this executes some_commandinside the new instance, then closes it. I want it to stay open.

但这some_command在新实例内执行,然后关闭它。我希望它保持开放。

One more detail which might affect answers: if I can get this to work I will use it in my .bashrcas alias(es), so bonus points for an aliasimplementation!

还有一个可能影响答案的细节:如果我能让它工作,我将在我的.bashrc别名中使用它,因此实现的奖励积分alias

回答by daveraja

This is a late answer, but I had the exact same problem and Google sent me to this page, so for completeness here is how I got around the problem.

这是一个迟到的答案,但我遇到了完全相同的问题,Google 将我发送到此页面,因此为了完整起见,这里是我解决问题的方法。

As far as I can tell, bashdoes not have an option to do what the original poster wanted to do. The -coption will always return after the commands have been executed.

据我所知,bash没有选择做原始海报想做的事情。该-c选项将始终在命令执行后返回。

Broken solution: The simplest and obvious attempt around this is:

破碎的解决方案:最简单和明显的尝试是:

bash -c 'XXXX ; bash'

This partly works (albeit with an extra sub-shell layer). However, the problem is that while a sub-shell will inherit the exported environment variables, aliases and functions are not inherited. So this might work for some things but isn't a general solution.

这部分有效(尽管有一个额外的子壳层)。然而,问题是虽然子 shell 会继承导出的环境变量,但不会继承别名和函数。所以这可能适用于某些事情,但不是通用的解决方案。

Better: The way around this is to dynamically create a startup file and call bash with this new initialization file, making sure that your new init file calls your regular ~/.bashrcif necessary.

更好:解决这个问题的方法是动态创建一个启动文件并使用这个新的初始化文件调用 bash,确保您的新 init 文件~/.bashrc在必要时调用您的常规文件。

# Create a temporary file
TMPFILE=$(mktemp)

# Add stuff to the temporary file
echo "source ~/.bashrc" > $TMPFILE
echo "<other commands>" >> $TMPFILE
echo "rm -f $TMPFILE" >> $TMPFILE

# Start the new bash shell 
bash --rcfile $TMPFILE

The nice thing is that the temporary init file will delete itself as soon as it is used, reducing the risk that it is not cleaned up correctly.

好消息是,临时 init 文件会在使用后立即自行删除,从而降低未正确清理的风险。

Note: I'm not sure if /etc/bashrc is usually called as part of a normal non-login shell. If so you might want to source /etc/bashrc as well as your ~/.bashrc.

注意:我不确定 /etc/bashrc 是否通常作为普通非登录 shell 的一部分被调用。如果是这样,您可能想要获取 /etc/bashrc 以及您的~/.bashrc.

回答by tripleee

You can pass --rcfileto Bash to cause it to read a file of your choice. This file will be read instead of your .bashrc. (If that's a problem, source ~/.bashrcfrom the other script.)

您可以传递--rcfile给 Bash 以使其读取您选择的文件。将读取此文件而不是您的.bashrc. (如果这是一个问题,请~/.bashrc从另一个脚本中获取。)

Edit:So a function to start a new shell with the stuff from ~/.more.shwould look something like:

编辑:所以用来自的东西启动一个新的shell的函数~/.more.sh看起来像:

more() { bash --rcfile ~/.more.sh ; }

... and in .more.shyou would have the commands you want to execute when the shell starts. (I suppose it would be elegant to avoid a separate startup file -- you cannot use standard input because then the shell will not be interactive, but you could create a startup file from a here document in a temporary location, then read it.)

...并且.more.sh您将拥有要在 shell 启动时执行的命令。(我认为避免使用单独的启动文件会很优雅——您不能使用标准输入,因为这样 shell 将无法交互,但您可以从临时位置的 here 文档创建一个启动文件,然后阅读它。)

回答by dashohoxha

Append to ~/.bashrca section like this:

附加到这样~/.bashrc的部分:

if [ "$subshell" = 'true' ]
then
    # commands to execute only on a subshell
    date
fi
alias sub='subshell=true bash'

Then you can start the subshell with sub.

然后你可以用sub.

回答by William Pursell

You can get the functionality you want by sourcing the script instead of running it. eg:

您可以通过获取脚本而不是运行它来获得所需的功能。例如:

$cat script
cmd1
cmd2
$ . script
$ at this point cmd1 and cmd2 have been run inside this shell

回答by Haimo

The accepted answer is really helpful! Just to add that process substitution (i.e., <(COMMAND)) is not supported in some shells (e.g., dash).

接受的答案真的很有帮助!只是要补充一点,<(COMMAND)在某些 shell(例如,dash)中不支持进程替换(即,)。

In my case, I was trying to create a custom action (basically a one-line shell script) in Thunar file manager to start a shell and activate the selected Python virtual environment. My first attempt was:

就我而言,我试图在 Thunar 文件管理器中创建一个自定义操作(基本上是一个单行 shell 脚本)来启动一个 shell 并激活选定的 Python 虚拟环境。我的第一次尝试是:

urxvt -e bash --rcfile <(echo ". $HOME/.bashrc; . %f/bin/activate;")

where %fis the path to the virtual environment handled by Thunar. I got an error (by running Thunar from command line):

%fThunar 处理的虚拟环境的路径在哪里。我收到一个错误(通过从命令行运行 Thunar):

/bin/sh: 1: Syntax error: "(" unexpected

Then I realized that my sh(essentially dash) does not support process substitution.

然后我意识到我的sh(基本上dash)不支持进程替换。

My solution was to invoke bashat the top level to interpret the process substitution, at the expense of an extra level of shell:

我的解决方案是bash在顶层调用来解释进程替换,代价是额外的 shell 级别:

bash -c 'urxvt -e bash --rcfile <(echo "source $HOME/.bashrc; source %f/bin/activate;")'


Alternatively, I tried to use here-document for dashbut with no success. Something like:

或者,我尝试使用 here-documentdash但没有成功。就像是:

echo -e " <<EOF\n. $HOME/.bashrc; . %f/bin/activate;\nEOF\n" | xargs -0 urxvt -e bash --rcfile

P.S.: I do not have enough reputation to post comments, moderators please feel free to move it to comments or remove it if not helpful with this question.

PS:我没有足够的声誉发表评论,版主请随时将其移至评论或如果对这个问题没有帮助,请将其删除。

回答by Rohan Ghige

With accordance with the answer by daveraja, here is a bash script which will solve the purpose.

根据daveraja的回答,这里有一个 bash 脚本可以解决这个问题。

Consider a situation if you are using C-shelland you want to execute a command without leaving the C-shell context/window as follows,

如果您正在使用C-shell并且想要在不离开 C-shell 上下文/窗口的情况下执行命令,请考虑一种情况,如下所示,

Command to be executed: Search exact word 'Testing' in current directory recursively only in *.h, *.c files

要执行的命令仅在 *.h、*.c 文件中递归搜索当前目录中的确切单词“Testing”

grep -nrs --color -w --include="*.{h,c}" Testing ./

Solution 1: Enter into bash from C-shell and execute the command

解决方案1:从C-shell进入bash并执行命令

bash
grep -nrs --color -w --include="*.{h,c}" Testing ./
exit

Solution 2: Write the intended command into a text file and execute it using bash

解决方案 2:将预期命令写入文本文件并使用 bash 执行它

echo 'grep -nrs --color -w --include="*.{h,c}" Testing ./' > tmp_file.txt
bash tmp_file.txt

Solution 3: Run command on the same line using bash

解决方案 3:使用 bash 在同一行上运行命令

bash -c 'grep -nrs --color -w --include="*.{h,c}" Testing ./'

Solution 4: Create a sciprt (one-time) and use it for all future commands

解决方案 4:创建一个 sciprt(一次性)并将其用于所有未来命令

alias ebash './execute_command_on_bash.sh'
ebash grep -nrs --color -w --include="*.{h,c}" Testing ./

The script is as follows,

脚本如下,

#!/bin/bash
# =========================================================================
# References:
# https://stackoverflow.com/a/13343457/5409274
# https://stackoverflow.com/a/26733366/5409274
# https://stackoverflow.com/a/2853811/5409274
# https://stackoverflow.com/a/2853811/5409274
# https://www.linuxquestions.org/questions/other-%2Anix-55/how-can-i-run-a-command-on-another-shell-without-changing-the-current-shell-794580/
# https://www.tldp.org/LDP/abs/html/internalvariables.html
# https://stackoverflow.com/a/4277753/5409274
# =========================================================================

# Enable following line to see the script commands
# getting printing along with their execution. This will help for debugging.
#set -o verbose

E_BADARGS=85

if [ ! -n "" ]
then
  echo "Usage: `basename 
bash -c some_command && another_command &
` grep -nrs --color -w --include=\"*.{h,c}\" Testing ." echo "Usage: `basename
$ bash --init-file <(echo 'some_command')
$ bash --rcfile <(echo 'some_command')
` find . -name \"*.txt\"" exit $E_BADARGS fi # Create a temporary file TMPFILE=$(mktemp) # Add stuff to the temporary file #echo "echo Hello World...." >> $TMPFILE #initialize the variable that will contain the whole argument string argList="" #iterate on each argument for arg in "$@" do #if an argument contains a white space, enclose it in double quotes and append to the list #otherwise simply append the argument to the list if echo $arg | grep -q " "; then argList="$argList \"$arg\"" else argList="$argList $arg" fi done #remove a possible trailing space at the beginning of the list argList=$(echo $argList | sed 's/^ *//') # Echoing the command to be executed to tmp file echo "$argList" >> $TMPFILE # Note: This should be your last command # Important last command which deletes the tmp file last_command="rm -f $TMPFILE" echo "$last_command" >> $TMPFILE #echo "---------------------------------------------" #echo "TMPFILE is $TMPFILE as follows" #cat $TMPFILE #echo "---------------------------------------------" check_for_last_line=$(tail -n 1 $TMPFILE | grep -o "$last_command") #echo $check_for_last_line #if tail -n 1 $TMPFILE | grep -o "$last_command" if [ "$check_for_last_line" == "$last_command" ] then #echo "Okay..." bash $TMPFILE exit 0 else echo "Something is wrong" echo "Last command in your tmp file should be removing itself" echo "Aborting the process" exit 1 fi

回答by raphtlw

Executing commands in a background shell

在后台 shell 中执行命令

Just add &to the end of the command, e.g:

只需添加&到命令的末尾,例如:

$ cat script
some_command
$ bash --init-file script

回答by x-yuri

$ bash -c 'some_command; exec bash'
$ sh -c 'some_command; exec sh'

In case you can't or don't want to use process substitution:

如果您不能或不想使用流程替换:

$ ENV=script sh

Another way:

其它的办法:

##代码##

sh-only way (dash, busybox):

sh- 唯一的方式(dashbusybox):

##代码##