Jak utrzymać działanie Bash po wykonaniu polecenia?


29

Chciałbym uruchomić coś takiego:

bash -c "some_program with its arguments"

ale żeby mieć interaktywny bash, działaj po some_programzakończeniu.

Na pewno -cnie jest to dobry sposób, jak się man bashwydaje:

Powłoka interaktywna to taka, która została uruchomiona bez argumentów niebędących opcjami i bez opcji -c

Jak to zrobić?

Główny cel opisano tutaj

UWAGA

  • Muszę zakończyć some_programOd czasu do czasu
  • Nie chcę tego umieszczać w tle
  • Chcę zostać na bash i zrobić coś innego
  • Chcę móc ponownie uruchomić program

1
jeśli celem jest ten kompleks, powinieneś to również wyjaśnić tutaj. ale to tylko rada
Kiwy

1
Próbowałem tu opisać jak najkrócej i bardzo dokładnie. Nie spodziewałem się jednak, że większość ludzi nie skupi się na szczegółach i spróbuje zaproponować coś innego. Zrobię notatki, żeby to wyjaśnić.
pawel7318

Po co ucieka terminal w tym innym pytaniu? Cel, który opisujesz, jest wykonalny, ale będzie wymagał obsługi we / wy. Twoja średnia podpowłoka nie będzie w stanie łatwo obsłużyć terminala, który uciekł do operacji we / wy za pomocą zwykłego pliku. Powinieneś zajrzeć dopty.
mikeserv

Nawiasem mówiąc, jedynym powodem, dla którego działa w mojej odpowiedzi poniżej, jest to, że kradnę terminal. W końcu jednak proces macierzysty może go cofnąć - i wtedy patrzysz na bufory 4kb.
mikeserv

Dlaczego nie chcesz umieścić programu w tle? Rozpocznij w tle, zrób trochę bash, umieść na pierwszym planie zfg
Bernhard

Odpowiedzi:


8
( exec sh -i 3<<SCRIPT 4<&0 <&3                                        
    echo "do this thing"
    echo "do that thing"
  exec  3>&- <&4
  SCRIPT
)

Lepiej jest to zrobić ze skryptu Jednak exec $0.jeśli jeden z tych deskryptorów plików skieruje się do nieużywanego urządzenia końcowego, to pomoże - musisz pamiętać, że inne procesy też chcą sprawdzić ten terminal.

A tak przy okazji, jeśli twoim celem jest, jak zakładam, zachowanie środowiska skryptu po jego wykonaniu, prawdopodobnie lepiej byś był z:

. ./script

Powłoki .doti bash's sourcenie są jednym i tym samym - powłoka .dotjest POSIX określona jako specjalna powłoka wbudowana i dlatego jest tak blisko zagwarantowania, jak to tylko możliwe, chociaż nie jest to w żaden sposób gwarancją, że będzie tam ...

Chociaż powyższe należy zrobić bez problemu. Na przykład możesz:

 ( exec sh -i 3<<SCRIPT 4<&0 <&3                                        
    echo "do this thing"
    echo "do that thing"
    $(cat /path/to/script)
    exec  3>&- <&4
    SCRIPT
 )

Powłoka uruchomi skrypt i wróci do interaktywnego monitu - o ile unikasz exitpowłoki ze skryptu, czyli procesu w tle - i połączy to twoje I / O z/dev/null.

PRÓBNY:

% printf 'echo "%s"\n' "These lines will print out as echo" \
    "statements run from my interactive shell." \
    "This will occur before I'm given the prompt." >|/tmp/script
% ( exec sh -i 3<<SCRIPT 4<&0 <&3
    echo "do this thing"
    echo "do that thing"
    $(cat /tmp/script)
    exec  3>&- <&4
SCRIPT
)
sh-4.3$ echo "do this thing"
    do this thing
sh-4.3$ echo "do that thing"
    do that thing
sh-4.3$ echo "These lines will print out as echo"
    These lines will print out as echo
sh-4.3$ echo "statements run from my interactive shell."
    statements run from my interactive shell.
sh-4.3$ echo "This will occur before I'm given the prompt."
    This will occur before I'm given the prompt.
sh-4.3$ exec  3>&- <&4
sh-4.3$

WIELE JOBS

Moim zdaniem powinieneś trochę lepiej zapoznać się z wbudowanymi opcjami zarządzania zadaniami powłoki. @Kiwy i @jillagre już się na to zwrócili w swoich odpowiedziach, ale może to wymagać dalszych szczegółów. A ja już wspomniano jedną POSIX określony specjalną powłokę wbudowany, ale set, jobs, fg,i bgto niewiele więcej, a jak pokazuje inna odpowiedź trapi killsą dwa jeszcze więcej.

Jeśli nie otrzymujesz natychmiastowych powiadomień o stanie równolegle działających procesów w tle, to dlatego, że twoje bieżące opcje powłoki są ustawione na wartość domyślną określoną przez POSIX -m, ale możesz je uzyskać asynchronicznie za pomocą set -b:

% man set
    b This option shall be supported if the implementation supports the
         User  Portability  Utilities  option. It shall cause the shell to
         notify the user asynchronously of background job completions. The
         following message is written to standard error:
             "[%d]%c %s%s\n", <job-number>, <current>, <status>, <job-name>

         where the fields shall be as follows:

         <current> The  character  '+' identifies the job that would be
                     used as a default for the fg or  bg  utilities;  this
                     job  can  also  be specified using the job_id "%+" or
                     "%%".  The character  '−'  identifies  the  job  that
                     would  become  the default if the current default job
                     were to exit; this job can also  be  specified  using
                     the  job_id  "%−".   For  other jobs, this field is a
                     <space>.  At most one job can be identified with  '+'
                     and  at  most one job can be identified with '−'.  If
                     there is any suspended  job,  then  the  current  job
                     shall  be  a suspended job. If there are at least two
                     suspended jobs, then the previous job also shall be a
   m  This option shall be supported if the implementation supports the
         User Portability Utilities option. All jobs shall be run in their
         own  process groups. Immediately before the shell issues a prompt
         after completion of the background job, a message  reporting  the
         exit  status  of  the background job shall be written to standard
         error. If a foreground job stops, the shell shall write a message
         to  standard  error to that effect, formatted as described by the
         jobs utility. In addition, if a job  changes  status  other  than
         exiting  (for  example,  if  it  stops  for input or output or is
         stopped by a SIGSTOP signal), the shell  shall  write  a  similar
         message immediately prior to writing the next prompt. This option
         is enabled by default for interactive shells.

Bardzo podstawową cechą systemów opartych na Uniksie jest ich sposób obsługi procesu signals. Czytałem kiedyś o pouczające artykuł na ten temat, który porównuje ten proces do opisu Douglasa Adamsa z planety NowWhat:

„W Poradniku Autostopowicza po Galaktyce Douglas Adams wspomina o wyjątkowo nudnej planecie, zamieszkałej przez grupę przygnębionych ludzi i pewną rasę zwierząt o ostrych zębach, które komunikują się z ludźmi, bardzo mocno gryząc je w uda. To uderzające podobny do UNIX-a, w którym jądro komunikuje się z procesami, wysyłając do nich paraliżujące lub śmiertelne sygnały. Procesy mogą przechwytywać niektóre sygnały i próbować dostosować się do sytuacji, ale większość z nich nie. ”

Odnosi się to do kill signals.

% kill -l 
> HUP INT QUIT ILL TRAP ABRT BUS FPE KILL USR1 SEGV USR2 PIPE ALRM TERM STKFLT CHLD CONT STOP TSTP TTIN TTOU URG XCPU XFSZ VTALRM PROF WINCH POLL PWR SYS

Przynajmniej dla mnie powyższy cytat odpowiedział na wiele pytań. Na przykład zawsze uważałem to za bardzo dziwne i wcale nie intuicyjne, że jeśli chcę monitorować ddproces, muszę to killzrobić. Po przeczytaniu miało to sens.

Powiedziałbym, że większość z nich nie próbuje się przystosować bez ważnego powodu - może to być o wiele bardziej irytujące niż byłoby dobrodziejstwem, gdyby kilka procesów spamowało twój terminal wszelkimi informacjami, które ich zdaniem deweloperów mogą być dla Ciebie ważne .

W zależności od konfiguracji terminala (którą możesz sprawdzić stty -a) ,CTRL+Z jest prawdopodobne jest ustawiony na przekazywanie SIGTSTPdo bieżącego lidera grupy procesów pierwszego planu, który jest prawdopodobnie twoją powłoką, i który powinien być również domyślnie skonfigurowany do traptego sygnału i zawiesić twoje ostatnie polecenie. Ponownie, jak pokazują razem odpowiedzi @jillagre i @Kiwy, nic nie stoi na przeszkodzie, aby dostosować tę funkcjonalność do własnych celów, tak jak lubisz.

SCREEN JOBS

Aby więc skorzystać z tych funkcji, należy je najpierw zrozumieć i dostosować ich obsługę do własnych potrzeb. Na przykład właśnie znalazłem ten screenrc na Github, który zawierascreen powiązania klawiszy dla SIGTSTP:

# hitting 'C-z C-z' will run Ctrl+Z (SIGTSTP, suspend as usual)
bind ^Z stuff ^Z

# hitting 'C-z z' will suspend the screen client
bind z suspend

Ułatwiłoby to zawieszenie procesu uruchomionego jako screenproces potomny lubscreen sam proces potomny zgodnie z życzeniem.

A zaraz potem:

% fg  

LUB:

% bg

Czy planujesz lub planujesz proces tak, jak chcesz. jobsWbudowaną może dostarczyć listę z nich w dowolnym momencie. Dodanie -loperandu będzie zawierało szczegóły pid.


Wygląda bardzo interesująco. Będę mógł przetestować to wszystko później dzisiaj.
pawel7318

23

Oto krótsze rozwiązanie, które spełnia Twoje oczekiwania, ale może nie mieć sensu, chyba że zrozumiesz problem i działanie bash:

bash -i <<< 'some_program with its arguments; exec </dev/tty'

Spowoduje to uruchomienie powłoki bash, uruchomienie some_program, a po some_programwyjściu zostaniesz przeniesiony do powłoki bash.

Zasadniczo to, co robimy, to podawanie ciosu na STDIN. Ten ciąg jest some_program with its arguments; exec </dev/tty. To mówi bashowi, aby some_programnajpierw uruchomił , a następnie uruchomił exec </dev/tty. Więc zamiast kontynuować czytanie poleceń z ciągu, który przekazujemy, bash rozpocznie czytanie /dev/tty.

Dzieje się -itak dlatego, że gdy bash się uruchamia, sprawdza, czy STDIN jest tty, a kiedy się uruchamia, nie jest. Ale później będzie, więc zmuszamy go do trybu interaktywnego.


Inne rozwiązanie

Innym pomysłem, o którym myślałem, że byłby bardzo przenośny, byłoby dodanie na samym końcu ~/.bashrcpliku.

if [[ -n "START_COMMAND" ]]; then
  start_command="$START_COMMAND"
  unset START_COMMAND
  eval "$start_command"
fi

Następnie, jeśli chcesz najpierw uruchomić powłokę za pomocą polecenia, po prostu wykonaj:

START_COMMAND='some_program with its arguments' bash

Wyjaśnienie:

Większość z tego powinna być oczywista, ale rezonsem dla rzeczy zmieniających nazwy zmiennych jest to, że możemy zlokalizować zmienną. Ponieważ $START_COMMANDjest to wyeksportowana zmienna, zostanie odziedziczona przez dowolne elementy potomne powłoki, a jeśli inna powłoka bash jest jednym z tych elementów potomnych, ponownie uruchomi polecenie. Przypisujemy więc wartość do nowej nieeksportowanej zmiennej ( $start_command) i usuwamy starą.


Wysadzić, jeśli jest więcej niż jeden co? Jedno polecenie? nie, powinien nadal działać. Masz jednak rację co do przenośności. Istnieją dwa czynniki, <<<nie jest to POSIX, ale możesz echo "string here" | bash -izamiast tego. Wtedy nie ma /dev/ttyco jest rzeczą Linux. Ale możesz zduplikować FD przed uruchomieniem basha, a następnie ponownie otworzyć STDIN, który wygląda podobnie do tego, co robisz, ale zdecydowałem się zachować prostotę.
Patrick,

ok ok, po prostu nie walcz. To po prostu działa dla mnie i robię wszystko, czego potrzebuję. Nie dbam zbytnio o POSIX i przenośność, potrzebuję go na moim pudełku i tylko tam. Sprawdzę również odpowiedź Mikeserva, ale nie mogę tego teraz zrobić.
pawel7318

1
Nie walczę :-). Szanuję odpowiedź mikserv. Poszedł do kompatybilności, ja poszedłem dla uproszczenia. Oba są całkowicie ważne. Czasami wybieram kompatybilność, jeśli nie jest zbyt skomplikowana. W tym przypadku nie sądziłem, że warto.
Patrick

2
@Patrick, /dev/ttyto nie Linux, ale zdecydowanie POSIX.
jlliagre,

2
Hej, co dziś wiesz .
Patrick

8

To powinno załatwić sprawę:

bash -c "some_program with its arguments;bash"

Edytować:

Oto nowa próba aktualizacji:

bash -c "
trap 'select wtd in bash restart exit; do [ \$wtd = restart ] && break || \$wtd ; done' 2
while true; do
    some_program with its arguments
done
"
  • Od czasu do czasu muszę zakończyć jakiś program

Użyj ControlC, zobaczysz to małe menu:

1) bash
2) restart
3) exit
  • Nie chcę tego umieszczać w tle

Tak jest w tym przypadku.

  • Chcę zostać na bashu, a potem zrobić coś innego

Wybierz opcję „bash”

  • Chcę móc ponownie uruchomić program

Wybierz opcję „uruchom ponownie”


nieźle, nieźle ... ale dobrze też byłoby wrócić do ostatniego polecenia. Jakieś pomysły na to? Sprawdź moje kolejne pytanie tutaj, aby zobaczyć, po co mi to potrzebne. Więc może zaproponujesz inne podejście
pawel7318

Spowoduje to wykonanie podpowłoki. Myślę, że pytający próbuje zachować środowisko skryptów.
mikeserv

dziękuję jlliagre - niezła próba, ale niezbyt przydatna dla mnie. Zazwyczaj naciskam ctrl + C i oczekuję, że zrobi to, co powinien. Dodatkowe menu to po prostu za dużo.
pawel7318

@ pawel7318 zachowanie CTRL+Cjest tylko efektem ubocznym domyślnej konfiguracji terminala, aby interpretować go jako SIGINT. Możesz to zmodyfikować według własnego uznania stty.
mikeserv

@ pawel7318 w celu dalszego wyjaśnienia, SIGINTjest trappedpowyżej z 2.
mikeserv

3

Możesz to zrobić, przekazując skrypt jako plik inicjujący:

bash --init-file foo.script

Lub możesz przekazać to w wierszu poleceń:

bash --init-file <(echo "ls -al")

Zauważ, że --init-filemiał on na celu odczytanie plików inicjujących dla całego systemu, /etc/bash.bashrctak abyś mógł chcieć je sourcezapisać w skrypcie.


0

Naprawdę nie widzę sensu tego robić, ponieważ po uruchomieniu tego programu powróciłeś do powłoki. Możesz jednak do tego:

bash -c "some_program with its arguments; bash"

Spowoduje to uruchomienie interaktywnej bash po uruchomieniu programu.


To uruchamia podpowłokę.
mikeserv

0

Możesz umieścić polecenie w tle, aby utrzymać bieżącą wersję basha otwartą:

some_program with its arguments &

Aby powrócić do działania, możesz użyć fgpolecenia i ^+zponownie umieścić je w tle


nie tym razem. Zakładasz, że chcę uruchomić ten bash ... z bash, co nie jest prawdą.
pawel7318

@ pawel7318, jeśli wyjaśnisz coś więcej, możemy dać ci lepszą odpowiedź, być może?
Kiwy,

Sprawdź moje kolejne pytanie tutaj .
pawel7318

@ pawel7318 jeśli twoje pytanie jest powiązane, dodaj link do drugiego pytania w swoim własnym pytaniu.
Kiwy,

1
@ pawel7318 bashczy nie, używasz dla mnie bardzo obcej powłoki, jeśli nie jest w stanie obsłużyć procesu tła. I zgadzam się z Kiwi - te informacje lepiej by ci służyły, gdyby były w twoim pytaniu.
mikeserv

0

Możesz użyć screena, aby uruchomić polecenie. Następnie możesz ponownie dołączyć do sesji po zakończeniu polecenia.

Alternatywnie, po prostu uruchom polecenie w tle some_program with its arguments&. Dzięki temu będziesz mógł ponownie uruchomić polecenie i uzyskać status polecenia po zakończeniu.


Uruchamiam go dokładnie z, screenale umieszczenie go w tle nie jest dla mnie przydatne - czasami muszę zakończyć program, zrobić coś innego i uruchomić go ponownie. A głównym celem jest zrobienie tego szybko.
pawel7318

@ pawel7318 Możesz zabić program działający w tle za pomocą polecenia kill %lub kill %1.
BillThor
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.