Назад | Перейти на главную страницу

Запустите интерактивную подоболочку bash с начальными командами, не возвращаясь сразу в («супер») оболочку.

Я хочу запустить подоболочку bash, (1) запустить несколько команд, (2) а затем остаться в этой подоболочке, чтобы делать то, что мне нравится. Я могу делать каждое из них индивидуально:

  1. Выполнить команду с помощью -c флаг:

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

    однако он сразу же возвращается в «супер» оболочку после выполнения команд. Я также могу просто запустить интерактивную подоболочку:

  2. Начать новый bash обработать:

    $> bash
    

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

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

    который работает, но не так, как я хотел, поскольку он запускает заданные команды в одной подоболочке, а затем открывает отдельную для взаимодействия.

Я хочу сделать это в одной строке. Как только я выйду из подоболочки, я должен без происшествий вернуться в обычную «супер» оболочку. Должен быть способ ~~

NB: Я не спрашиваю ...

  1. не спрашивая, где взять справочную страницу bash
  2. не спрашивая, как читать команды инициализации из файла ... Я знаю, как это сделать, это не то решение, которое я ищу
  3. не заинтересован в использовании tmux или gnu screen
  4. не заинтересован в придании этому контекста. То есть, вопрос предназначен для общего характера, а не для какой-либо конкретной цели.
  5. если возможно, я хочу избегать использования обходных путей, которые позволяют добиться того, чего я хочу, но «грязным» способом. Я просто хочу сделать это в одной строке. В частности, я не хочу делать что-то вроде xterm -e 'ls'

Это легко сделать с помощью временные именованные каналы:

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

Кредит за этот ответ идет на комментарий от Ли Райан. Я нашел это действительно полезным и менее заметным в комментариях, поэтому подумал, что это должен быть отдельный ответ.

Вы можете сделать это окольным путем с помощью временного файла, хотя это займет две строки:

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

Попробуйте вместо этого:

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

$SHELL Открывает оболочку в интерактивном режиме, ожидая закрытия с exit.

Почему бы не использовать собственные подоболочки?

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

Заключение команд в круглые скобки заставляет bash порождать подпроцесс для запуска этих команд, так что вы можете, например, изменить среду, не затрагивая родительскую оболочку. Это в основном более читаемый эквивалент 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 подходить больше, так как нет необходимости играть с экранирующими строками.

«Ожидаемое решение», о котором я говорил, - это программирование оболочки bash с Ожидайте языка программирования:

#!/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"

Если sudo -E bash не работает, я использую следующее, что до сих пор оправдало мои ожидания:

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

Я установил HOME = $ HOME, потому что я хочу, чтобы в моем новом сеансе HOME был установлен на HOME моего пользователя, а не на HOME пользователя root, что происходит по умолчанию в некоторых системах.

менее элегантно, чем --init-file, но, возможно, более инструментально:

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

while read -a commands
do
    eval ${commands[@]}
done

Я делаю то же самое, просто используя сценарий, обычно с целью установки переменных среды для использования в конкретном каталоге проекта;

$ cat shell.sh
#!/bin/bash
export PATH=$PWD/bin:$PATH
export USERNAME=foo
export PASSWORD=bar
export DB_SERVER=http://localhost:6001
bash

$ echo ${USERNAME:-none}
none

$ ./shell.sh

$ echo $USERNAME
foo

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

$ bash --init-file <(echo 'ls; pwd')
$ bash --rcfile <(echo 'ls; pwd')

Если вы не можете использовать замену процесса:

$ cat script
ls; pwd
$ bash --init-file script

С участием sh (dash, busybox):

$ ENV=script sh

Или:

$ bash -c 'ls; pwd; exec bash'
$ sh -c 'ls; pwd; exec sh'