Запустіть інтерактивну підзаглушку bash з початковими командами, не повертаючись до («супер») оболонки негайно


86

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

  1. Запустити команду за допомогою -cпрапора:

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

    однак він негайно повертається до «супер» оболонки після виконання команд. Я також можу просто запустити інтерактивну підканал:

  2. Почати новий bashпроцес:

    $> bash

    і він не вийде з підшару, поки я не скажу так прямо ... але я не можу запускати жодних початкових команд. Найближче рішення, яке я знайшов:

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

    яка працює, але не так, як я цього хотів, оскільки вона виконує задані команди в одній підгруппі, а потім відкриває окрему для взаємодії.

Я хочу це зробити в одному рядку. Як тільки я вийду з нижньої оболонки, я повинен повернутися до звичайної "супер" оболонки без випадків. Має бути спосіб ~~

NB: Що я не прошу ...

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

Я можу уявити очікуване рішення, але це навряд чи той, який вам потрібен. Яким способом exec bashрішення для вас не підходить?
Глен Джекман

@glennjackman вибачте, я не знайомий з жаргоном. Що таке "очікуване рішення"? Також exec bashрішення включає два окремих підрозділи. Я хочу одну безперервну підскладку.
SABBATINI Лука

3
Краса execполягає в тому, що вона замінює першу нижню частину корпусу на другу, тож у вас залишилася лише 1 оболонка нижче батьківської. Якщо ваші команди ініціалізації встановлюють змінні середовища, вони існуватимуть у виконаній оболонці.
Гленн Джекман


2
І проблема execполягає в тому, що ви втрачаєте все, що не передається на підпрограми через навколишнє середовище, наприклад, неекспортовані змінні, функції, псевдоніми, ...
Curt J. Sampson,

Відповіді:


77

Це легко зробити за допомогою тимчасово названих труб :

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

За цю відповідь заслуговує коментар Лі Раяна . Я вважав це дійсно корисним, і це менш помітно в коментарях, тому я подумав, що це має бути власна відповідь.


9
Це, мабуть, означає, що $HOME/.bashrcвоно не виконується. Це повинно бути включене з тимчасово названої труби.
Hubro

9
Для уточнення, щось на кшталт цього:bash --init-file <(echo ". \"$HOME/.bashrc\"; ls; pwd")
Hubro

5
Це так грубо, але це працює. Я не можу повірити, що Баш не підтримує це безпосередньо.
Пат Німейер

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в нього.
Едуардо Іванець

Едуардо, дякую. Це приємне рішення, але ... ти кажеш, що цього неможливо зробити, не поспішаючи з файлом вводу / виводу. Є очевидні причини, чому я вважаю за краще зберігати це як самодостатня команда, тому що файли моментів, що входять в суміш, мені доведеться почати турбуватися про те, як зробити випадкові тимчасові файли, а потім, як ви вже згадували, видалити їх. Це просто вимагає набагато більше зусиль таким чином, якщо я хочу бути суворим. Звідси прагнення до більш мінімалістичного, елегантного рішення.
SABBATINI Лука

1
@SABBATINILuca: Я нічого такого не кажу. Це просто спосіб і mktempвирішує проблему з тимчасовим файлом, як вказував @cjc. Bash міг би підтримувати читання команд init від stdin, але наскільки я можу сказати, це не так. Спеціалізація -як файл init та трубопровід їх наполовину працює, але Bash потім виходить (можливо, тому, що він виявив конвеєр). Елегантним рішенням IMHO є використання exec.
Едуардо Іванець

1
Це також не перевершує вашу нормальну ініціалізацію? @SABBATINILuca Що ви намагаєтеся досягти з цим, чому вам потрібно нереститися оболонкою автоматично, запускаючи деякі команди, а потім тримати цю оболонку відкритою?
користувач9517

11
Це старе питання, але Bash може створювати тимчасові іменовані труби, використовуючи такий синтаксис: bash --init-file <(echo "ls; pwd").
Лі Лі Райан

5

Спробуйте це замість цього:

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

$SHELL Це робить оболонку відкритою в інтерактивному режимі, чекаючи закриття exit.


9
FYI, після цього відкривається нова оболонка, тож якщо будь-яка з команд впливає на стан поточної оболонки (наприклад, пошук файлу), вона може працювати не так, як очікувалося
ThiefMaster

3

"Очікуване рішення", про яке я мав на увазі, - це програмування оболонки 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 / профіль?
muhuk

Підтверджено, що це дозволяє вводити команди в історію, чого не мають інші рішення. Це чудово підходить для запуску процесу у файлі .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більше подобається підхід, оскільки немає необхідності грати з втечами струн.


1
Я не впевнений, чи є якісь застереження, що використовують exec для сценарію, який має на увазі ОП, але для мене це далеко не найкраще запропоноване рішення, оскільки воно використовує прості команди 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.