Uruchom interaktywną podpowłokę bash z początkowymi poleceniami bez natychmiastowego powrotu do powłoki („super”)


86

Chcę uruchomić podpowłokę bash, (1) uruchomić kilka poleceń, (2), a następnie pozostać w tej podpowłoce, aby zrobić tak, jak mi się podoba. Mogę wykonać każdą z tych czynności indywidualnie:

  1. Uruchom polecenie za pomocą -cflagi:

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

    jednak natychmiast powraca do „super” powłoki po wykonaniu poleceń. Mogę również uruchomić interaktywną podpowłokę:

  2. Rozpocznij nowy bashproces:

    $> bash

    i nie opuści podpowłoki, dopóki nie powiem tak wyraźnie ... ale nie mogę uruchomić żadnych początkowych poleceń. Najbliższym rozwiązaniem, jakie znalazłem, jest:

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

    co działa, ale nie tak, jak chciałem, ponieważ uruchamia podane polecenia w jednej podpowłoce, a następnie otwiera osobne dla interakcji.

Chcę to zrobić w jednym wierszu. Po wyjściu z podpowłoki powinienem wrócić do zwykłej „super” powłoki bez żadnych incydentów. Musi być jakiś sposób ~~

NB: O co nie pytam ...

  1. nie pytając, skąd wziąć stronę podręcznika bash
  2. nie pytam, jak czytać polecenia inicjujące z pliku ... Wiem jak to zrobić, to nie jest rozwiązanie, którego szukam
  3. nie jestem zainteresowany używaniem ekranu tmux lub gnu
  4. nie zainteresowany nadaniem kontekstu temu. Tj. Pytanie ma być ogólne, a nie w żadnym konkretnym celu
  5. jeśli to możliwe, chcę uniknąć stosowania obejść, które pozwalają osiągnąć to, czego chcę, ale w „brudny” sposób. Chcę to zrobić tylko w jednej linii. W szczególności nie chcę robić czegoś takiegoxterm -e 'ls'

Mogę sobie wyobrazić oczekiwane rozwiązanie, ale nie jest to tak naprawdę jeden liniowiec. W jaki sposób exec bashrozwiązanie jest dla Ciebie nieodpowiednie?
glenn jackman

@glennjackman przepraszam, nie znam żargonu. Co to jest „Oczekiwanie na rozwiązanie”? Ponadto, exec bashrozwiązanie obejmuje dwa oddzielne podpowłok. Chcę jedną ciągłą podpowłokę.
SABBATINI Luca,

3
Piękno execpolega na tym, że zastępuje pierwszą podpowłokę drugą, więc pozostała tylko 1 powłoka poniżej rodzica. Jeśli komendy inicjujące ustawiają zmienne środowiskowe, będą one istnieć w wykonanej powłoce.
glenn jackman


2
Problem execpolega na tym, że tracisz wszystko, co nie jest przekazywane do podpowłoki przez środowisko, takie jak nieeksportowane zmienne, funkcje, aliasy, ...
Curt J. Sampson

Odpowiedzi:


77

Można to łatwo zrobić za pomocą tymczasowych nazwanych potoków :

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

Podziękowania dla tej odpowiedzi należą do komentarza Lie Ryan . Uznałem to za bardzo przydatne i jest mniej zauważalne w komentarzach, więc pomyślałem, że powinna to być własna odpowiedź.


9
Prawdopodobnie oznacza $HOME/.bashrcto jednak, że nie jest wykonywany. Musiałby być dołączony z tymczasowej nazwy potoku.
Hubro

9
Aby wyjaśnić, coś takiego:bash --init-file <(echo ". \"$HOME/.bashrc\"; ls; pwd")
Hubro

5
To takie obrzydliwe, ale działa. Nie mogę uwierzyć, że bash nie obsługuje tego bezpośrednio.
Pat Niemeyer

2
@Gus, .jest synonimem sourcepolecenia: ss64.com/bash/source.html .
Jonathan Potter

1
Czy istnieje sposób, aby działał z przełączaniem użytkowników, takim jak sudo bash --init-file <(echo "ls; pwd")lub sudo -iu username bash --init-file <(echo "ls; pwd")?
jeremysprofile

10

Możesz to zrobić na okrągło z plikiem tymczasowym, chociaż zajmie to dwie linie:

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

Aby uzyskać ładny efekt, możesz usunąć plik tymczasowy, włączając rm $BASH_SOURCEgo.
Eduardo Ivanec,

Eduardo, dziękuję. To miłe rozwiązanie, ale ... czy mówisz, że nie można tego zrobić bez konieczności majstrowania przy plikach I / O. Są oczywiste powody, dla których wolałbym zachować to jako samodzielne polecenie, ponieważ w momencie, gdy pliki wejdą w skład miksu, będę musiał zacząć się martwić, jak tworzyć losowe pliki tymczasowe, a następnie, jak wspomniałeś, usunąć je. To po prostu wymaga o wiele więcej wysiłku, jeśli chcę być rygorystyczny. Stąd potrzeba bardziej minimalistycznego, eleganckiego rozwiązania.
SABBATINI Luca,

1
@SABBATINILuca: Nie mówię czegoś takiego. To tylko sposób na mktemprozwiązanie problemu z plikiem tymczasowym, jak wskazał @cjc. Bash mógłby wesprzeć czytanie komend inicjujących ze standardowego wejścia, ale o ile wiem, że nie. Określanie -jako plik init i przesyłanie ich do potoku w połowie działa, ale Bash następnie kończy działanie (prawdopodobnie dlatego, że wykrył potok). Eleganckim rozwiązaniem IMHO jest użycie exec.
Eduardo Ivanec,

1
Czy to również nie zastępuje normalnej inicjalizacji bash? @SABBATINILuca, co próbujesz osiągnąć dzięki czemu musisz odradzać powłokę, automatycznie uruchamiać niektóre polecenia, a następnie utrzymywać tę powłokę otwartą?
user9517,

11
To stare pytanie, ale Bash może tworzyć tymczasowe nazwane potoki, używając następującej składni: bash --init-file <(echo "ls; pwd").
Lie Ryan

5

Spróbuj zamiast tego:

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

$SHELL Powoduje, że powłoka jest otwarta w trybie interaktywnym, czekając na zamknięcie exit.


9
Do Twojej wiadomości, to otwiera następnie nową powłokę, więc jeśli którekolwiek z poleceń wpłyną na stan bieżącej powłoki (np.
Pobieranie

3

„Oczekiwane rozwiązanie”, o którym mówiłem, to programowanie powłoki bash za pomocą języka programowania 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"

Urodziłbyś się tak: ./subshell.exp "ls; pwd"


Myślę, że miałoby to tę zaletę, że rejestruje polecenia również w historii, czy się mylę? Jestem też ciekawy, czy w tym przypadku zostanie wykonane bashrc / profile?
muhuk

Potwierdzono, że pozwala to na umieszczanie poleceń w historii, czego nie oferują inne rozwiązania. Jest to świetne do rozpoczęcia procesu w pliku .screenrc - jeśli wyjdziesz z rozpoczętego procesu, nie zamkniesz okna ekranu.
Dan Sandberg

1

Dlaczego nie użyć natywnych podpowłok?

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

Dołączenie poleceń w nawiasach powoduje, że bash spawn jest procesem podrzędnym do uruchamiania tych poleceń, dzięki czemu można na przykład zmieniać środowisko bez wpływu na powłokę nadrzędną. Jest to w zasadzie bardziej czytelny odpowiednik bash -c "ls; pwd; exec $BASH".

Jeśli nadal wygląda to na pełne, istnieją dwie opcje. Jednym z nich jest posiadanie tego fragmentu jako funkcji:

$ 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
$

Innym jest exec $BASHskrócenie:

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

Osobiście Rbardziej podoba mi się podejście, ponieważ nie trzeba bawić się uciekającymi strunami.


1
Nie jestem pewien, czy istnieją jakieś zastrzeżenia dotyczące wykonania w scenariuszu, który OP ma na myśli, ale dla mnie jest to zdecydowanie najlepsze zaproponowane rozwiązanie, ponieważ używa prostych poleceń bash bez żadnych problemów z ucieczką łańcucha.
Peter

1

Jeśli sudo -E bashnie działa, korzystam z następujących, które do tej pory spełniły moje oczekiwania:

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

Ustawiam HOME = $ HOME, ponieważ chcę, aby moja nowa sesja miała HOME ustawiony na HOME mojego użytkownika, a nie na HOME, co dzieje się domyślnie w niektórych systemach.


0

mniej elegancki niż --init-file, ale może bardziej instrumentalny:

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

while read -a commands
do
    eval ${commands[@]}
done
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.