Як тримати Bash після виконання команди?


29

Я хотів би запустити щось подібне:

bash -c "some_program with its arguments"

але мати інтерактивний баш продовжувати працювати після some_programзакінчення.

Я впевнений, що -cце не так добре, як man bashseys:

Інтерактивна оболонка - це запускається без необов'язкових аргументів і без параметра -c

То як це зробити?

Основна мета описана тут

ПРИМІТКА

  • Мені потрібно some_programчас від часу припиняти
  • Я не хочу відкладати це на другий план
  • Я хочу залишитися на bashтодішньому, щоб зробити щось інше
  • Я хочу мати змогу запустити програму ще раз

1
якщо мета така складна, ви також повинні пояснити це тут. але це лише
порада

1
Я намагався описати тут якомога коротше і дуже точно. Але я не сподівався, що більшість людей не зосередиться на деталях, а спробують запропонувати щось інше. Я покладу кілька записок, щоб це було зрозуміло.
pawel7318

Для чого в цьому іншому питанні втікає термінал? Ціль, яку ви описуєте, виконується, але вона потребує керування введенням. Ваша середня додаткова оболонка не збирається легко обробляти вхідний / вхідний термінал через звичайний файл. Ви повинні заглянути вpty.
mikeserv

Єдиною причиною, що працює у моїй відповіді нижче, до речі, є те, що я краду термінал. Зрештою, проте, батьківський процес, ймовірно, поверне його назад - і тоді ви дивитесь на 4 кб буфери.
mikeserv

Чому ви не хочете ставити програму на задній план? Почніть це на задньому плані, зробіть башти, поставте його на перший планfg
Бернхард

Відповіді:


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

Це краще зробити зі сценарію, хоча за допомогою exec $0.Or, якщо один з цих дескрипторів файлів спрямовує до термінального пристрою, який наразі не використовується, це допоможе - ви повинні пам’ятати, що інші процеси теж хочуть перевірити цей термінал.

І до речі, якщо ваша мета, як я припускаю, це зберегти середовище сценарію після його виконання, вам, ймовірно, буде набагато краще:

. ./script

Оболонки .dotі bash's sourceне одне і те саме - оболонка .dotPOSIX визначена як спеціальна вбудована оболонка, і тому наближається до гарантії, яку ви можете отримати, хоча це аж ніяк не гарантія, що вона буде там ...

Хоча вищезазначене має робити так, як ви очікуєте, з невеликим питанням. Наприклад, ви можете:

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

Оболонка запустить ваш сценарій і поверне вас до інтерактивного підказки - до тих пір, поки ви не будете exitвикористовувати оболонку зі свого сценарію, тобто не буде фоновим процесом, - це зв’яже ваш і / о/dev/null.

DEMO:

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

МНОГО JOBS

На мою думку, вам слід ознайомитись трохи з вбудованими параметрами управління завданнями оболонки. @Kiwy та @jillagre обидва вже торкнулися цього у своїх відповідях, але це може бути підставою для отримання детальної інформації. І я вже говорив один POSIX-зазначену спеціальну оболонку вбудований, але set, jobs, fg,і bgще кілька, і, як показує іншу відповідь trapі killще два по- , як і раніше.

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

Дуже фундаментальною особливістю систем на базі Unix є їх метод керування процесом signals. Я колись прочитав просвітницьку статтю на тему, яка порівнює цей процес з описом планети Дугласа Адамса. Що тепер:

"У Посібнику автостопом по Галактиці Дуглас Адамс згадує надзвичайно тупу планету, населену купою депресивних людей і певну породу тварин з гострими зубами, які спілкуються з людьми, кусаючи їх дуже сильно в стегна. Це вражає. подібний до UNIX, в якому ядро ​​спілкується з процесами, надсилаючи до них паралізуючі або смертоносні сигнали. Процеси можуть перехоплювати деякі сигнали та намагатися адаптуватися до ситуації, але більшість з них цього не робить ".

Це стосується 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

Принаймні для мене, наведена цитата відповіла на безліч питань. Наприклад, я завжди вважав це дуже дивним і зовсім не інтуїтивним, що якщо я хотів би відслідковувати ddпроцес, я мав killце робити . Прочитавши, що це мало сенс.

Я б сказав, що більшість з них не намагаються адаптуватися з поважних причин - це може бути набагато більшим роздратуванням, ніж було б користю мати безліч процесів, що розсипають ваш термінал будь-якою інформацією, яку їх розробники думали, що може бути важливою для вас .

Залежно від конфігурації вашого терміналу (яку ви можете перевірити stty -a) , CTRL+Zшвидше за все , буде встановлено, щоб переслати a SIGTSTPпоточному лідеру групи переднього плану, який, ймовірно, ваша оболонка, і який також повинен бути налаштований за замовчуванням на trapцей сигнал і призупинити останню команду. Знову ж таки, як показують відповіді @jillagre та @Kiwy разом, жодне перешкоджає вам адаптувати цю функціональність за вашим призначенням.

SCREEN JOBS

Отож, щоб скористатися цими можливостями, очікується, що ви спочатку їх зрозумієте та налаштуєте їх обробку під власні потреби. Наприклад, я щойно знайшов цей скриншот на Github, який включає screenприв'язку ключів для 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

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

І відразу після цього:

% fg  

АБО:

% bg

Буде передній план чи фоновий процес, як вам зручніше. jobsВбудований може надати вам список з них в будь-який момент. Додавання -lоперанду буде включати дані про pid.


Виглядає дуже цікаво. Я можу протестувати все це пізніше сьогодні.
pawel7318

23

Ось коротше рішення, яке виконує те, що ви хочете, але може не мати сенсу, якщо ви не зрозумієте проблему та як працює баш:

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

Це запустить bash shell, почнеться some_program, і після some_programвиходу ви перейдете до bash shell.

В основному те, що ми робимо, це годувати баш-ланцюжок на STDIN. Ця струна є some_program with its arguments; exec </dev/tty. Це говорить some_programспочатку bash запустити , а потім запустити exec </dev/tty. Тож замість того, щоб продовжувати читати команди з рядка, який ми передаємо, bash почне читати з /dev/tty.

Це -iтому, що коли запускається bash, він перевіряє, чи STDIN - це tty, а коли він запускається - ні. Але згодом воно буде, тому ми змушуємо його в інтерактивному режимі.


Ще одне рішення

Ще одна ідея, яку я вважав, що це дуже переносимо, - це додати наступне в самому кінці вашого ~/.bashrcфайлу.

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

Потім, коли ви хочете спочатку запустити оболонку з командою, просто виконайте:

START_COMMAND='some_program with its arguments' bash

Пояснення:

Більшість цього має бути очевидним, але резонанс для зміни змінних імен змінних даних є таким, що ми можемо локалізувати змінну. Оскільки $START_COMMANDце експортована змінна, вона буде успадкована будь-якими дітьми оболонки, і якщо інша оболонка bash є одним із цих дітей, вона знову запустить команду. Тож ми присвоюємо значення новій неекспортованій змінній ( $start_command) та видаляємо стару.


Удар, якщо більше одного, що? Одна команда? ні, все одно повинні працювати. Ти прав, що стосується портативності. Там є 2 фактори, <<<це не POSIX, але ви можете echo "string here" | bash -iзамість цього. Тоді є /dev/ttyріч Linux. Але ви можете скопіювати FD перед запуском bash, а потім повторно відкрити STDIN, який схожий на те, що ви робите, але я вирішив все просто.
Патрік

ок ок, просто не бійся. Це просто працює на мене і роби все, що мені потрібно. Мені не дуже важливо POSIX і портативність, мені це потрібно в моїй коробці і тільки там. Я також перевірю відповідь mikeserv, але зараз не можу цього зробити.
pawel7318

1
Не воює :-). Я поважаю відповідь міксерва. Він пішов на сумісність, я пішов на простоту. Обидва цілком дійсні. Іноді я піду на сумісність, якщо вона не надто складна. У цьому випадку я не вважав, що цього варто.
Патрік

2
@Patrick, /dev/ttyце не річ Linux, але, безумовно, POSIX.
jlliagre


8

Для цього слід зробити фокус:

bash -c "some_program with its arguments;bash"

Редагувати:

Ось нова спроба після оновлення:

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
"
  • Мені потрібно час від часу припиняти деяку програму

Використовуйте ControlC, вам буде представлено це невелике меню:

1) bash
2) restart
3) exit
  • Я не хочу відкладати це на другий план

Це так.

  • Я хочу залишитися на башті потім зробити щось інше

Виберіть вибір "баш"

  • Я хочу мати змогу запустити програму ще раз

Виберіть вибір «перезапустити»


непогано не погано .. але добре було б також мати можливість повернутися до останньої команди. Якісь ідеї для цього? Будь ласка, перевірте ще одне моє запитання тут, щоб побачити, що для цього мені потрібно. Тож, можливо, ви запропонуєте інший підхід
pawel7318

Це виконує підзаголовок. Я думаю, що запитувач намагається зберегти середовище сценарію.
mikeserv

дякую jlliagre - приємна спроба, але не дуже корисна для мене. Я зазвичай натискаю ctrl + C, і я очікую, що він просто зробить те, що він повинен зробити. Додаткове меню просто багато.
pawel7318

@ pawel7318 поведінка CTRL+C- лише побічний ефект конфігурації за замовчуванням вашого терміналу для інтерпретації його як a SIGINT. Ви можете змінити це так, як і будете stty.
mikeserv

@ Pawel7318 для подальшого уточнення, SIGINTзнаходиться trappedвище 2.
mikeserv

3

Ви можете зробити це, передавши свій скрипт як файл ініціалізації:

bash --init-file foo.script

Або ви можете передати його в командному рядку:

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

Зауважте, що --init-fileвін призначений для читання файлів ініціалізації на всій системі, наприклад, /etc/bash.bashrcщоб ви могли " source" їх у своєму сценарії.


0

Я не бачу сенсу робити це, оскільки ви вже повертаєтесь до оболонки після запуску цієї програми. Однак ви могли до цього:

bash -c "some_program with its arguments; bash"

Це запустить інтерактивний баш після запуску програми.


Це запускає нижню частину корпусу.
mikeserv

0

Ви можете поставити команду у фоновий режим, щоб ваш поточний баш був відкритим:

some_program with its arguments &

Для повернення до запущеного ви можете потім скористатися fgкомандою і ^+zзнову поставити її на задній план


не цього разу. Ви припускаєте, що я хочу запустити цей баш ... від bash, що не так.
pawel7318

@ pawel7318, якщо ви поясніть трохи більше, ми могли б дати вам кращу відповідь?
Ківі

Будь ласка, перевірте моє ще одне запитання тут .
pawel7318

@ pawel7318, якщо ваше питання пов’язане, додайте посилання на інше питання у власному запитанні.
Kiwy

1
@ pawel7318 bashчи ні, ви використовуєте для мене дуже чужу оболонку, якщо вона не може обробити фоновий процес. І я погоджуюся з Ківі - ця інформація допомогла б вам краще, якби вона була у вашому питанні.
mikeserv

0

Ви можете використовувати екран для запуску команди. Потім ви можете повторно долучити до сеансу після завершення команди.

Крім того, просто запустіть команду у фоновому режимі some_program with its arguments&. Це дозволить вам перезапустити команду та отримати статус комісії після її виконання.


Я запускаю саме його, screenале розміщення його на задньому плані для мене не корисне - мені іноді потрібно припинити програму, зробити щось інше і запустити її знову. І головна мета - зробити це швидко.
pawel7318

@ pawel7318 Ви можете вбити фонову програму командою kill %або kill %1.
BillThor
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.