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

87

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

  1. Запустите команду, используя -cфлаг:

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

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

  2. Начать новый bashпроцесс:

    $> bash

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

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

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

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

NB: что я не спрашиваю ...

  1. не спрашивая, где взять man-страницу bash
  2. не спрашиваю, как читать команды инициализации из файла ... я знаю, как это сделать, это не решение, которое я ищу
  3. не заинтересованы в использовании экрана tmux или gnu
  4. не заинтересованы в предоставлении контекста для этого. Т.е. вопрос должен быть общим, а не для какой-то конкретной цели
  5. если возможно, я хочу избегать использования обходных путей, которые выполняют то, что я хочу, но «грязным» способом. Я просто хочу сделать это в одной строке. В частности, я не хочу делать что-то вродеxterm -e 'ls'
САББАТИНИ Лука
источник
Я могу вообразить решение Expect, но вряд ли это будет тот лайнер, который вам нужен. Какое exec bashрешение вам не подходит?
Гленн Джекман
@glennjackman извините, я не знаком с жаргоном. Что такое «ожидаемое решение»? Кроме того, exec bashрешение включает в себя две отдельные подоболочки. Я хочу одну сплошную подоболочку.
САББАТИНИ Лука
3
Прелесть в execтом, что он заменяет первый подоболочек вторым, поэтому вы оставляете только одну оболочку ниже родительского. Если ваши команды инициализации устанавливают переменные окружения, они будут существовать в исполняемой оболочке.
Гленн Джекман
2
возможно, такой же stackoverflow.com/questions/7120426/…
Сиро Сантилли 新疆 改造 中心 法轮功 六四 事件
2
И проблема в execтом, что вы теряете все, что не передается в субоболочки через окружение, такие как неэкспортируемые переменные, функции, псевдонимы, ...
Курт Дж. Сэмпсон,

Ответы:

78

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

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

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

Джонатан Поттер
источник
9
Это, вероятно, означает, что $HOME/.bashrcэто не выполняется, хотя. Это должно быть включено из временного именованного канала.
Hubro
9
Чтобы уточнить, как то так:bash --init-file <(echo ". \"$HOME/.bashrc\"; ls; pwd")
Hubro
5
Это так грубо, но это работает. Я не могу поверить, что Bash не поддерживает это напрямую.
Пэт Нимейер,
2
@Gus, .это синоним sourceкоманды: ss64.com/bash/source.html .
Джонатан Поттер
1
Есть ли способ заставить его работать с переключением пользователей, например sudo bash --init-file <(echo "ls; pwd")или sudo -iu username bash --init-file <(echo "ls; pwd")?
jeremysprofile
10

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

echo "ls; pwd" > initfile
bash --init-file initfile
Эдуардо Иванец
источник
Для хорошего эффекта вы можете заставить временный файл удалить себя, включив rm $BASH_SOURCEв него.
Эдуардо Иванец
Эдуардо, спасибо. Это хорошее решение, но ... вы говорите, что это не может быть сделано без возни с файловым вводом / выводом. Есть очевидные причины, по которым я бы предпочел оставить это как отдельную команду, потому что, как только файлы попадают в микс, мне придется начать беспокоиться о том, как создавать случайные временные файлы, а затем, как вы упоминали, их удалять. Это просто требует гораздо больше усилий, если я хочу быть строгим. Отсюда и стремление к более минималистичному, элегантному решению.
САББАТИНИ Лука
1
@SABBATINILuca: Я не говорю ничего подобного. Это всего лишь способ, и mktempон решает проблему временного файла, как указал @cjc. Bash может поддерживать чтение команд init из stdin, но, насколько я могу судить, этого не делает. Указание в -качестве файла инициализации и передача их наполовину работает, но затем Bash завершает работу (возможно, потому, что обнаружил конвейер). ИМХО элегантным решением является использование exec.
Эдуардо Иванец
1
Разве это не отменяет вашу обычную инициализацию bash? @SABBATINILuca Чего вы пытаетесь достичь с помощью этого, почему вам нужно порождать оболочку, автоматически запускать некоторые команды и затем держать эту оболочку открытой?
user9517
11
Это старый вопрос, но Bash может создавать временные именованные каналы, используя следующий синтаксис: bash --init-file <(echo "ls; pwd").
Ли Райан
5

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

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

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

ExpoBi
источник
9
Кстати, после этого открывается новая оболочка, поэтому, если какая-либо из команд влияет на текущее состояние оболочки (например, поиск файла), она может работать не так, как ожидалось
ThiefMaster,
3

«Решение Expect», о котором я говорил, - это программирование оболочки bash на языке программирования Expect :

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

Гленн Джекман
источник
Я полагаю, что в этом тоже было бы преимущество регистрации команд в истории, я не прав? Также мне любопытно, если bashrc / профиль выполняется в этом случае?
Мухук
Подтвердили, что это позволяет вам помещать команды в историю, чего нет в других решениях. Это отлично подходит для запуска процесса в файле .screenrc - если вы выходите из запущенного процесса, вы не закрываете окно экрана.
Дэн Сандберг
1

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

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

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

toriningen
источник
1
Я не уверен, есть ли какие-нибудь предостережения, использующие exec для сценария, который имеет в виду OP, но для меня это гораздо лучшее предлагаемое решение, потому что оно использует простые команды bash без проблем с выходом строки.
Петр
1

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

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

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

Яго
источник
0

менее элегантный --init-file, но, возможно, более удобный:

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

while read -a commands
do
    eval ${commands[@]}
done
RubyTuesdayDONO
источник