Como manter o Bash em execução após a execução do comando?

25

Gostaria de executar algo assim:

bash -c "some_program with its arguments"

mas para que um bash interativo continue em exibição depois que some_program terminar.

Tenho certeza de que -c não é uma boa maneira de man bash seys:

An interactive shell is one started without non-option arguments and without the -c option

Então, como fazer isso?

O objetivo principal é descrito aqui

OBSERVAÇÃO

  • preciso terminar some_program periodicamente
  • Eu não quero colocá-lo em segundo plano
  • Eu quero ficar no bash para fazer outra coisa
  • Eu quero poder executar o programa novamente
por pawel7318 04.04.2014 / 13:06

7 respostas

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

Isso é melhor feito a partir de um script com exec $0. . Ou se um desses descritores de arquivo direcionar para um dispositivo de terminal que não está sendo usado, ele ajudará - você precisa lembrar, outros processos querem verificar esse terminal, também.

E, a propósito, se seu objetivo for preservar o ambiente do script depois de executá-lo, provavelmente você será muito mais bem servido com:

. ./script

Os .dot e bash's source do shell não são um e o mesmo - o .dot do shell é POSIX especificado como um shell especial integrado e, portanto, está tão próximo de ser garantido quanto possível, embora isso não seja feito significa uma garantia de que estará lá ...

Embora o acima deve fazer o que você espera com pouco problema. Por exemplo, você pode:

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

O shell executará seu script e retornará você ao prompt interativo - contanto que você evite exit ing o shell do seu script, ou seja, o fundo do processo - que vinculará sua i / o a /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$

MUITOS JOBS

É minha opinião que você deve ficar um pouco mais familiarizado com as opções de gerenciamento de tarefas embutidas do shell. @Kiwy e @jillagre já mencionaram isso em suas respostas, mas isso pode exigir mais detalhes. E eu já mencionei um shell especial POSIX especificado embutido, mas set, jobs, fg, e bg são mais alguns, e, como outra resposta demonstra trap e kill são mais dois ainda.

Se você ainda não está recebendo notificações instantâneas sobre o status de execução simultânea de processos em segundo plano, é porque suas opções de shell atuais estão definidas para o padrão de -m especificado pelo POSIX, mas você pode obtê-las de forma assíncrona com set -b em vez disso:

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

Uma característica muito importante dos sistemas baseados em Unix é o método de processamento do processo signals . Uma vez li um artigo esclarecedor sobre o assunto que compara este processo à descrição de Douglas Adams sobre o planeta NowWhat :

"In The Hitchhiker's Guide to the Galaxy, Douglas Adams mentions an extremely dull planet, inhabited by a bunch of depressed humans and a certain breed of animals with sharp teeth which communicate with the humans by biting them very hard in the thighs. This is strikingly similar to UNIX, in which the kernel communicates with processes by sending paralyzing or deadly signals to them. Processes may intercept some of the signals, and try to adapt to the situation, but most of them don't."

Isto está se referindo a 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

Pelo menos para mim, a citação acima respondeu a muitas perguntas. Por exemplo, eu sempre considerei muito estranho e nada intuitivo que, se eu quisesse monitorar um processo dd , eu tivesse que kill dele. Depois de ler isso fazia sentido.

Eu diria que a maioria deles não tenta se adaptar por um bom motivo - pode ser um aborrecimento muito maior do que seria um bocado ter um monte de processos spamming seu terminal com qualquer informações que seus desenvolvedores acharam que poderiam ter sido importantes para você.

Dependendo da sua configuração do terminal (que você pode verificar com stty -a ) , o CTRL+Z provavelmente está definido para encaminhar um SIGTSTP para o líder do grupo de processos do primeiro plano atual, o que provavelmente é shell, e que também deve ser configurado por padrão para trap desse sinal e suspender seu último comando. Novamente, como as respostas de @jillagre e @Kiwy juntas mostram, não há como impedi-lo de adaptar essa funcionalidade ao seu propósito como preferir.

SCREEN JOBS

Portanto, para aproveitar esses recursos, é esperado que você os entenda e personalize o tratamento de acordo com suas próprias necessidades. Por exemplo, acabei de encontrar esta screenrc no Github que inclui screen key- ligações para 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

Isso simplifica a suspensão de um processo em execução como um processo screen child ou o processo filho screen como desejado.

E imediatamente depois:

% fg  

OR:

% bg

Primeiro ou segundo plano do processo, conforme você preferir. O jobs integrado pode fornecer uma lista desses a qualquer momento. Adicionar o operando -l incluirá detalhes do pid.

    
por 19.02.2018 / 16:37
21

Aqui está uma solução mais curta que realiza o que você quer, mas pode não fazer sentido a menos que você entenda o problema e como o bash funciona:

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

Isso iniciará um shell bash, iniciará some_program e, após some_program , você será descartado para um bash shell.

Basicamente, o que estamos fazendo é alimentar uma sequência em STDIN. Essa string é some_program with its arguments; exec </dev/tty . Isso diz ao bash para lançar some_program primeiro e depois executar exec </dev/tty depois. Então, ao invés de continuar a ler os comandos da string que nós passamos, o bash começará a ler a partir de /dev/tty .

O -i é porque quando o bash é iniciado, ele verifica se STDIN é um tty e quando inicia não é. Mas depois será, então forçamos isso no modo interativo.

Outra solução

Outra ideia que pensei seria muito portátil seria adicionar o seguinte ao final do arquivo ~/.bashrc .

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

Então, quando você quiser iniciar um shell com um comando primeiro, faça:

START_COMMAND='some_program with its arguments' bash

Explicação:

A maior parte disso deve ser óbvia, mas a ressonância para a mudança de nome de variável é para que possamos localizar a variável. Como $START_COMMAND é uma variável exportada, ela será herdada por qualquer filho do shell, e se outro shell bash for um desses filhos, ele executará o comando novamente. Então, atribuímos o valor a uma nova variável não exportada ( $start_command ) e excluímos a antiga.

    
por 04.04.2014 / 21:59
7

Isso deve funcionar:

bash -c "some_program with its arguments;bash"

Editar:

Aqui está uma nova tentativa após sua atualização:

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
"
  • I need to terminate some_program from time to time

Use Controle C , você verá este pequeno menu:

1) bash
2) restart
3) exit
  • I don't want to put it to the background

Esse é o caso.

  • I want to stay on the bash then to do something else

Selecione a opção "bash"

  • I want to be able to run the program again

Selecione a opção "reiniciar"

    
por 04.04.2014 / 13:13
1

Você pode fazer isso passando o script como um arquivo de inicialização:

bash --init-file foo.script

Ou você pode passá-lo na linha de comando:

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

Observe que --init-file foi destinado à leitura de arquivos de inicialização em todo o sistema, como /etc/bash.bashrc , para que você possa querer ' source ' no seu script.

    
por 29.08.2018 / 09:56
0

Eu realmente não vejo o ponto de fazer isso, já que você já volta para um shell depois de executar este programa. No entanto, você poderia para isso:

bash -c "some_program with its arguments; bash"

Isso iniciará uma festa interativa depois que o programa for executado.

    
por 04.04.2014 / 13:13
0

Você pode colocar o comando em segundo plano para manter sua festa atual aberta:

some_program with its arguments &

Para retornar à execução, você poderia usar o comando fg e ^+z para colocá-lo novamente em segundo plano

    
por 04.04.2014 / 13:25
0

Você pode querer usar a tela para executar o comando. Você pode então reconectar à sessão após o comando ser concluído.

Como alternativa, basta executar o comando no background some_program with its arguments& . Isso permitirá que você execute novamente o comando e obtenha o status do comando assim que estiver pronto.

    
por 04.04.2014 / 14:04

Tags