Как сохранить работоспособность Bash после выполнения команды?

29

Я хотел бы запустить что-то вроде этого:

bash -c "some_program with its arguments"

но иметь интерактивный bash продолжать работать после some_programконцов.

Я уверен, что -cэто не очень хорошо, как man bashСейс:

Интерактивная оболочка запускается без аргументов без опций и без опции -c

Так как это сделать?

Основная цель описана здесь

НОТА

  • Мне нужно прекратить some_programвремя от времени
  • Я не хочу ставить это на задний план
  • Я хочу остаться на bashто, чтобы сделать что-то еще
  • Я хочу иметь возможность снова запустить программу
pawel7318
источник
1
если цель настолько сложна, вы также должны объяснить это здесь. но это всего лишь совет
Kiwy
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.Или, если один из этих файловых дескрипторов направляет на терминальное устройство, которое в данный момент не используется, это поможет - вы должны помнить, что другие процессы тоже хотят проверить этот терминал.

И, кстати, если ваша цель, как я полагаю, заключается в том, чтобы сохранить среду сценария после ее выполнения, вам, вероятно, будет гораздо лучше справляться с:

. ./script

Оболочки .dotи bash's sourceне одно и то же - оболочка - .dotэто POSIX, заданная как специальная встроенная оболочка, и поэтому она настолько близка к гарантированности, насколько вы можете получить, хотя это ни в коем случае не является гарантией того, что она будет там ...

Хотя вышесказанное должно делать, как вы ожидаете, с небольшой проблемой. Например, вы можете:

 ( 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скорее всего, он настроен на перенаправление SIGTSTPтекущего лидера группы процессов переднего плана, который, вероятно, является вашей оболочкой, и который также должен быть настроен по умолчанию на trapэтот сигнал и приостановить вашу последнюю команду. Опять же, как показывают ответы @jillagre и @Kiwy вместе, вы не сможете помешать вам адаптировать эту функциональность к вашим целям по вашему усмотрению.

SCREEN JOBS

Поэтому, чтобы воспользоваться этими функциями, вы должны сначала понять их и настроить их обработку в соответствии с вашими потребностями. Например, я только что нашел этот screenrc на 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.

mikeserv
источник
Выглядит очень интересно. Я смогу проверить все это позже сегодня.
pawel7318
23

Вот более короткое решение, которое выполняет то, что вы хотите, но может не иметь смысла, если вы не понимаете проблему и то, как работает bash:

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

Это запустит оболочку bash, запустится some_program, и после some_programвыхода вы перейдете в оболочку bash.

В основном то, что мы делаем, - это добавляем bash в строку STDIN. Эта строка some_program with its arguments; exec </dev/tty. Это говорит bash some_programсначала запустить , а затем запустить 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
@ Патрик, /dev/ttyэто не вещь Linux, но определенно POSIX.
jlliagre
2
Привет, что ты знаешь .
Патрик
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
"
  • Мне нужно время от времени завершать some_program

Используйте ControlC, вам будет представлено это маленькое меню:

1) bash
2) restart
3) exit
  • Я не хочу ставить это на задний план

Это тот случай.

  • Я хочу остаться на баш, чтобы заняться чем-то другим

Выберите "bash" выбор

  • Я хочу иметь возможность снова запустить программу

Выберите «перезагрузить» выбор

jlliagre
источник
неплохо не плохо .. но было бы неплохо иметь возможность вернуться к этой последней команде. Есть идеи для этого? Пожалуйста, проверьте мой другой вопрос здесь, чтобы увидеть, зачем мне это нужно. Так что, возможно, вы предложите другой подход
pawel7318
Это выполняет подоболочку. Я думаю, что спрашивающий пытается сохранить среду сценария.
mikeserv
спасибо, jlliagre - хорошая попытка, но не очень полезная для меня. Я обычно нажимаю Ctrl + C и ожидаю, что он просто сделает то, что должен. Дополнительное меню просто много.
pawel7318
@ pawel7318 поведение CTRL+Cявляется лишь побочным эффектом конфигурации вашего терминала по умолчанию, чтобы интерпретировать его как 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«это» в вашем скрипте

jeroent
источник
0

Я не вижу смысла делать это, так как вы уже возвращаетесь в оболочку после запуска этой программы. Тем не менее, вы могли бы к этому:

bash -c "some_program with its arguments; bash"

Это запустит интерактивный bash после запуска программы.

mtak
источник
Это запускает подоболочку.
mikeserv
0

Вы можете поместить команду в фоновом режиме, чтобы сохранить текущий текущий bash:

some_program with its arguments &

Чтобы вернуться к работе, вы можете использовать fgкоманду и ^+zснова поместить ее в фоновый режим.

Kiwy
источник
не в этот раз. Вы предполагаете, что я хочу запустить этот bash ... из bash, что не так.
pawel7318
@ pawel7318 если ты объяснишь немного больше, мы могли бы дать тебе лучший ответ, может быть, тоже?
Киви
Пожалуйста, проверьте мой другой вопрос здесь .
pawel7318
@ pawel7318, если ваш вопрос связан, пожалуйста, добавьте ссылку на другой вопрос в вашем собственном вопросе.
14:00
1
@ pawel7318 bashили нет, вы используете очень инопланетную оболочку для меня, если она не справляется с фоновым процессом. И я согласен с Киви - эта информация послужила бы вам лучше, если бы она была в вашем вопросе.
mikeserv
0

Вы можете использовать экран для запуска команды. Затем вы можете присоединиться к сеансу после завершения команды.

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

BillThor
источник
Я запускаю его точно из, screenно перенести его в фоновый режим для меня бесполезно - иногда мне нужно завершить программу, сделать что-то еще и запустить ее снова. И главная цель - сделать это быстро.
pawel7318
@ pawel7318 Вы можете убить фоновую программу с помощью команды kill %или kill %1.
BillThor