Я хочу запустить подоболочку bash, (1) запустить несколько команд, (2) а затем остаться в этой подоболочке, чтобы делать то, что мне нравится. Я могу делать каждое из них индивидуально:
Выполнить команду с помощью -c
флаг:
$> bash -c "ls; pwd; <other commands...>"
однако он сразу же возвращается в «супер» оболочку после выполнения команд. Я также могу просто запустить интерактивную подоболочку:
Начать новый bash
обработать:
$> bash
и он не выйдет из подоболочки, пока я не скажу об этом явно ... но я не могу запускать никакие начальные команды. Ближайшее решение, которое я нашел:
$> bash -c "ls; pwd; <other commands>; exec bash"
который работает, но не так, как я хотел, поскольку он запускает заданные команды в одной подоболочке, а затем открывает отдельную для взаимодействия.
Я хочу сделать это в одной строке. Как только я выйду из подоболочки, я должен без происшествий вернуться в обычную «супер» оболочку. Должен быть способ ~~
NB: Я не спрашиваю ...
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'