Как вызвать bash, запустить команды внутри новой оболочки, а затем вернуть управление пользователю? - PullRequest
55 голосов
/ 19 августа 2011

Это должно быть либо очень просто, либо действительно сложно, но я ничего не могу найти в этом ... Я пытаюсь открыть новый экземпляр bash, затем выполнить несколько команд внутри него и вернуть управление элементу управления.пользователь внутри того же экземпляра .

Я попытался:

$ bash -lic "some_command"

, но он выполняет some_command внутри нового экземпляра, а затем закрывает его.Я хочу, чтобы он оставался открытым.

Еще одна деталь, которая может повлиять на ответы: если я смогу заставить это работать, я буду использовать его в своем .bashrc как псевдоним (ы), поэтому бонусные баллы за alias реализация!

Ответы [ 7 ]

39 голосов
bash --rcfile <(echo '. ~/.bashrc; some_command')

обходится без создания временных файлов. Вопрос на других сайтах:

33 голосов
/ 12 сентября 2013

Это поздний ответ, но у меня возникла точно такая же проблема, и Google отправил меня на эту страницу, поэтому для полноты здесь я нашел способ решить эту проблему.

Насколько я могу судить, у bash нет возможности сделать то, что хотел сделать оригинальный постер. Опция -c всегда будет возвращаться после выполнения команд.

Неисправное решение : самая простая и очевидная попытка обойти это:

bash -c 'XXXX ; bash'

Это частично работает (хотя и с дополнительным подоболочечным слоем). Однако проблема заключается в том, что, хотя вложенная оболочка будет наследовать экспортированные переменные среды, псевдонимы и функции не наследуются. Так что это может работать для некоторых вещей, но не является общим решением.

Лучше : способ обойти это динамически создать файл запуска и вызвать bash с этим новым файлом инициализации, убедившись, что ваш новый файл инициализации при необходимости вызывает ваш обычный ~ / .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

Приятно то, что временный файл инициализации будет удален сам, как только он будет использован, что уменьшит риск того, что он не будет очищен правильно.

Примечание: я не уверен, что / etc / bashrc обычно вызывается как часть обычной оболочки без регистрации. Если это так, вы можете использовать / etc / bashrc, а также ~ / .bashrc.

4 голосов
/ 19 августа 2011

Вы можете передать --rcfile Bash, чтобы он прочитал файл по вашему выбору. Этот файл будет прочитан вместо вашего .bashrc. (Если это проблема, источник ~/.bashrc из другого скрипта.)

Редактировать: Так что функция для запуска новой оболочки с материалом из ~/.more.sh будет выглядеть примерно так:

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

... и в .more.sh у вас будут команды, которые вы хотите выполнить при запуске оболочки. (Я полагаю, было бы элегантно избежать отдельного файла запуска - вы не можете использовать стандартный ввод, потому что тогда оболочка не будет интерактивной, но вы можете создать файл запуска из документа здесь во временном расположении, а затем прочитать его.)

2 голосов
/ 08 июля 2018

В соответствии с ответом daveraja , вот скрипт bash, который решит цель.

Рассмотрим ситуацию, если вы используете C-shell и хотите выполнить команду не выходя из контекста / окна C-оболочки следующим образом,

Команда для выполнения : Поиск точного слова «Проверка» в текущем каталоге рекурсивно только в * .h, * .c файлах

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

Решение 1 : войти в bash из C-shell и выполнить команду

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 : Создайте sciprt (единовременно) и используйте его для всех будущих команд

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 голосов
/ 19 августа 2011

Вы можете получить желаемую функциональность, используя скрипт вместо его запуска.например:

$cat script
cmd1
cmd2
$ . script
$ at this point cmd1 and cmd2 have been run inside this shell
1 голос
/ 01 июля 2019

Принятый ответ действительно полезен! Просто добавить, что подстановка процесса (т.е. <(COMMAND)) не поддерживается в некоторых оболочках (например, dash).

В моем случае я пытался создать настраиваемое действие (в основном, однострочный скрипт оболочки) в файловом менеджере Thunar, чтобы запустить оболочку и активировать выбранную виртуальную среду Python. Моя первая попытка была:

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

где %f - это путь к виртуальной среде, обрабатываемой Thunar. Я получил ошибку (при запуске Thunar из командной строки):

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

Тогда я понял, что мой sh (по сути dash) не поддерживает подстановку процессов.

Мое решение состояло в том, чтобы вызвать bash на верхнем уровне, чтобы интерпретировать подстановку процесса за счет дополнительного уровня оболочки:

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

В качестве альтернативы я попытался использовать здесь-документ для dash, но безуспешно. Что-то вроде:

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

P.S .: У меня недостаточно репутации, чтобы оставлять комментарии, модераторы, пожалуйста, не стесняйтесь перемещать их в комментарии или удалять, если они не помогают в этом вопросе.

1 голос
/ 12 сентября 2015

Добавить к ~/.bashrc раздел, подобный следующему:

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

Затем вы можете запустить подоболочку с sub.

...