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


85

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

我试过了:

$ bash -lic "some_command"

但这会some_command在新实例内部执行,然后将其关闭。我希望它保持打开状态。

还有一个细节可能会影响答案:如果我可以使用它,我.bashrc将以别名的形式使用它,因此,alias实现的奖励点!


我不了解有关别名的最后一部分。别名在当前shell的上下文中执行,因此它们没有您要解决的问题(尽管出于多种原因,通常功能比别名更好)。
2011年

2
我希望能够alias shortcut='bash -lic "some_command"'在.bashrc中放入类似内容,然后运行shortcut并运行一个some_command已经运行的新shell 。
费利克斯Saparelli

Answers:


62

我试图在Windows 10中执行此操作(尝试编写批处理“启动”文件/脚本),但我得到了The system cannot find the file specified.
Blue Doctor Doctor

1
@Scott逐步调试它:1)cat ~/.bashrc有效吗?2)cat <(echo a)有效吗?3)bash --rcfile somefile有效吗?
西罗Santilli郝海东冠状病六四事件法轮功

1
@Scott我们只是在WSL上遇到了这个问题(从启动批处理文件启动bash)。我们的解决方案是转义一些字符,以便批处理可以理解它:bash.exe --rcfile ^<^(echo 'source $HOME/.bashrc; ls; pwd'^)应从Windows命令提示符下运行。
user2561747

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


39

这是一个较晚的答案,但是我遇到了完全相同的问题,Google将该页面发送到了我,因此为了完整起见,我是如何解决该问题的。

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

无效的解决方案:围绕此问题最简单,最明显的尝试是:

bash -c 'XXXX ; bash'

这部分起作用(尽管有一个额外的子壳层)。但是,问题在于,尽管子外壳程序将继承导出的环境变量,但别名和函数不会被继承。因此,这可能对某些事情有用,但不是通用解决方案。

更好:解决此问题的方法是动态创建启动文件,并使用此新的初始化文件调用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

令人高兴的是,临时init文件在使用后会立即删除,从而降低了无法正确清理的风险。

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


rm -f $TMPFILE事使得它。我实际上不记得我使用过的用例,现在我使用了更高级的自动化技术,但这是必须的方法!
费利克斯Saparelli

7
没有可替换进程的tmp文件bash --rcfile <(echo ". ~/.bashrc; a=b")
Ciro Santilli郝海东冠状病六四事件法轮功

3
仅当您的Bash脚本成功运行时,临时文件才会被清除。一个更可靠的解决方案是使用trap
三人房

5

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

编辑:因此,从中开始使用东西启动新shell的函数应~/.more.sh类似于:

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

...,并且.more.sh您将拥有要在Shell启动时执行的命令。(我想避免使用单独的启动文件是很不错的选择-您不能使用标准输入,因为外壳将不会是交互式的,但是您可以在临时位置从here文档创建启动文件,然后读取它。)


3

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

$ cat脚本
命令1
命令2
$。脚本
此时,$ cmd1和cmd2已在此shell中运行

嗯 那行得通,但是没有办法将所有内容都嵌入到中alias=''吗?
费利克斯Saparelli

1
切勿使用别名;使用函数代替。您可以输入:'foo(){cmd1; cmd2; }'放在启动脚本中,然后执行foo将在当前shell中运行这两个命令。请注意,这些解决方案都不能为您提供新的外壳程序,但是您可以先执行“ exec bash”然后执行“ foo”
William Pursell,2011年

1
永不说永不。别名有自己的位置
glenn jackman 2011年

有没有用函数代替别名的用例示例?
威廉·珀塞尔

3

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

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

然后,您可以使用来启动子外壳sub


创新点。别名本身可能更好一些env subshell=true bash(因为不会泄漏$subshell到后续命令中):使用env使用export
费利克斯Saparelli

你是对的。但是我注意到它也没有env效果。为了检查是否已定义,我使用echo $subshell(而不是env | grep subshell您使用的)。
dashohoxha 2015年

3

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

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

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

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

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

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

我的解决方案是bash在顶层调用以解释进程替换,但要付出额外的外壳程序代价:

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

另外,我尝试将here-document用于,dash但没有成功。就像是:

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

附言:我没有足够的声誉来发表评论,主持人请随时将其移至评论,或者如果对该问题没有帮助,请将其删除。


如果不希望bash出现在顶部(或没有bash),则有许多文献记载的方法可以直接实现流程替换,主要是使用fifos。
费利克斯Saparelli

2

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

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

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

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

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

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

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

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

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

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

解决方案4:一次性创建一个密码,并将其用于以后的所有命令

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

脚本如下,

#!/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 "$1" ]
then
  echo "Usage: `basename $0` grep -nrs --color -w --include=\"*.{h,c}\" Testing ."
  echo "Usage: `basename $0` 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

2
对于完全不同的问题,这是完全不同的答案。(很高兴您知道了这一点,但它不属于该主题。如果此站点上不存在此主题,请考虑用第二句话来打开一个新问题,然后立即用其余的答案自己回答...这就是应该如何处理的方式)。另请参见Ciro Santilli的答案,以了解一种完全不构建临时文件的方法。
费利克斯Saparelli

标记为“不回答”,因为它不是尝试回答这个问题(尽管这是回答另一个问题的非常好尝试!)
Charles Duffy

0

这是另一个(有效的)变体:

这将打开一个新的gnome终端,然后在新终端中运行bash。首先读取用户的rc文件,然后ls -la在新的shell变为交互式之前将命令发送给新的shell执行。最后一个回显添加了完成执行所需的额外换行符。

gnome-terminal -- bash -c 'bash --rcfile <( cat ~/.bashrc; echo ls -la ; echo)'

我还发现有时装饰终端很有用,例如使用颜色以更好地定向。

gnome-terminal --profile green -- bash -c 'bash --rcfile <( cat ~/.bashrc; echo ls -la ; echo)'

-1

在后台shell中执行命令

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

bash -c some_command && another_command &

-1
$ bash --init-file <(echo 'some_command')
$ bash --rcfile <(echo 'some_command')

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

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

其他方式:

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

sh仅方法(dashbusybox):

$ ENV=script sh

好的替代方法,但是将从顶部(在ENV=script零件之前)除去或重新措词而受益,以避免与顶部答案的公然复制粘贴混淆。
费利克斯Saparelli

坦率地说,@FélixSaparelli所有的解决方案,但其他答案中都带有ENV+ sh,但大多数情况下它们被埋在成堆的文本中,或者被不需要/不必要的代码包围。我相信,进行简短,清晰的总结将对所有人都有好处。
x-yuri
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.