Como iniciar "corretamente" um aplicativo a partir de um shell

21

Acho difícil formular a pergunta com precisão, mas darei o meu melhor. Eu uso dwmcomo meu gerenciador de janelas padrão edmenucomo meu iniciador de aplicativos. Eu quase não uso aplicativos GUI além do meu navegador. A maior parte do meu trabalho é feita diretamente na linha de comando. Além disso, sou um grande fã do minimalismo em relação a sistemas operacionais, aplicativos etc. Uma das ferramentas de que nunca me livrei foi o iniciador de aplicativos. Principalmente porque me falta uma compreensão precisa de como os ativadores de aplicativos funcionam / o que eles fazem. Até uma extensa pesquisa na Internet mostra apenas uma explicação vaga. O que eu quero fazer é me livrar do iniciador de aplicativos, porque, além de gerar o aplicativo, não tenho absolutamente nenhum uso para ele. Para fazer isso, eu realmente gostaria de saber como "corretamente" iniciar aplicativos a partir do shell. Por meio do qual o significado de "corretamente" pode ser aproximado por "como faria o iniciador de aplicativos".

Conheço as seguintes maneiras de gerar processos do shell:

  1. exec /path/to/Program substituir shell pelo comando especificado sem criar um novo processo
  2. sh -c /path/to/Program iniciar processo dependente do shell
  3. /path/to/Program iniciar processo dependente do shell
  4. /path/to/Program 2>&1 & processo independente do shell de lançamento
  5. nohup /path/to/Program & inicie o processo independente do shell e redirecione a saída para nohup.out

Atualização 1: Eu posso ilustrar o que, por exemplo dmenu, reconstruí-lo de chamadas repetidas para ps -efldiferentes condições. Ele gera um novo shell /bin/bashe, como filho desse shell, o aplicativo /path/to/Program. Enquanto a criança estiver por perto, a concha estará por perto. (Como ele gerencia isso está além de mim ...) Por outro lado, se você emitir nohup /path/to/Program &um shell /bin/bash, o programa se tornará filho desse shell, mas se você sair desse shell, o pai do programa será o processo mais alto. Portanto, se o primeiro processo foi, por exemplo, /sbin/init verbosee já foi PPID 1, ele será o pai do programa. Aqui está o que eu tentei explicar usando um gráfico: chromiumfoi lançado via dmenu, firefoxfoi lançado usando exec firefox & exit:

systemd-+-acpid
        |-bash---chromium-+-chrome-sandbox---chromium-+-chrome-sandbox---nacl_helper
        |                 |                           `-chromium---5*[chromium-+-{Chrome_ChildIOT}]
        |                 |                                                    |-{Compositor}]
        |                 |                                                    |-{HTMLParserThrea}]
        |                 |                                                    |-{OptimizingCompi}]
        |                 |                                                    `-3*[{v8:SweeperThrea}]]
        |                 |-chromium
        |                 |-chromium-+-chromium
        |                 |          |-{Chrome_ChildIOT}
        |                 |          `-{Watchdog}
        |                 |-{AudioThread}
        |                 |-3*[{BrowserBlocking}]
        |                 |-{BrowserWatchdog}
        |                 |-5*[{CachePoolWorker}]
        |                 |-{Chrome_CacheThr}
        |                 |-{Chrome_DBThread}
        |                 |-{Chrome_FileThre}
        |                 |-{Chrome_FileUser}
        |                 |-{Chrome_HistoryT}
        |                 |-{Chrome_IOThread}
        |                 |-{Chrome_ProcessL}
        |                 |-{Chrome_SafeBrow}
        |                 |-{CrShutdownDetec}
        |                 |-{IndexedDB}
        |                 |-{LevelDBEnv}
        |                 |-{NSS SSL ThreadW}
        |                 |-{NetworkChangeNo}
        |                 |-2*[{Proxy resolver}]
        |                 |-{WorkerPool/1201}
        |                 |-{WorkerPool/2059}
        |                 |-{WorkerPool/2579}
        |                 |-{WorkerPool/2590}
        |                 |-{WorkerPool/2592}
        |                 |-{WorkerPool/2608}
        |                 |-{WorkerPool/2973}
        |                 |-{WorkerPool/2974}
        |                 |-{chromium}
        |                 |-{extension_crash}
        |                 |-{gpu-process_cra}
        |                 |-{handle-watcher-}
        |                 |-{inotify_reader}
        |                 |-{ppapi_crash_upl}
        |                 `-{renderer_crash_}
        |-2*[dbus-daemon]
        |-dbus-launch
        |-dhcpcd
        |-firefox-+-4*[{Analysis Helper}]
        |         |-{Cache I/O}
        |         |-{Cache2 I/O}
        |         |-{Cert Verify}
        |         |-3*[{DOM Worker}]
        |         |-{Gecko_IOThread}
        |         |-{HTML5 Parser}
        |         |-{Hang Monitor}
        |         |-{Image Scaler}
        |         |-{JS GC Helper}
        |         |-{JS Watchdog}
        |         |-{Proxy R~olution}
        |         |-{Socket Thread}
        |         |-{Timer}
        |         |-{URL Classifier}
        |         |-{gmain}
        |         |-{localStorage DB}
        |         |-{mozStorage #1}
        |         |-{mozStorage #2}
        |         |-{mozStorage #3}
        |         |-{mozStorage #4}
        |         `-{mozStorage #5}
        |-gpg-agent
        |-login---bash---startx---xinit-+-Xorg.bin-+-xf86-video-inte
        |                               |          `-{Xorg.bin}
        |                               `-dwm-+-dwmstatus
        |                                     `-xterm---bash-+-bash
        |                                                    `-pstree
        |-systemd---(sd-pam)
        |-systemd-journal
        |-systemd-logind
        |-systemd-udevd
        |-wpa_actiond
        `-wpa_supplicant

Atualização 2: Acho que a pergunta também pode ser resumida em: Qual deve ser o pai de um processo? Por exemplo, deveria ser uma concha ou deveria ser o initprocesso, ou seja, o processo com PID 1?

lord.garbage
fonte
3
A resposta concisa à sua pergunta seria "o que obtiver os resultados desejados".
Wayne Werner
11
caramba, lixo - você faz algumas boas perguntas. mas acho que Wayne está no nariz aqui - sua última edição pergunta init- para qual a resposta pode ser ... talvez? depende de como / se você planeja conversar com ele, o initque usa e onde estão os canais de dados. Em geral, essas coisas tendem a se resolver - inité para isso. De qualquer forma, geralmente quando você daemon um processo init. Ou, se você deseja controle do trabalho, shell atual.
mikeserv
Hahaha, felicidades @mikeserv; 4:37 da manhã aqui e já é a primeira risada do dia. É verdade que essas coisas sempre dão certo. Vou remover dmenue ver como me dou bem com o que aprendi. Acho exec /path/to/Program & exitou /bin/bash -c /path/to/Program & exitpara ser bastante utilizável. Mas todos eles fazem, 1ou seja, inito pai do Programque está bem comigo, desde que isso faça sentido e não viole nenhum *nixprincípio básico .
Lr.garbage 27/08
@ lord.garbage - é porque você exec &, eu acho. Normalmente faço minhas coisas no terminal ... talvez você se aproveite da pergunta de Ben Crowell aqui . Eu tenho uma resposta lá, mas todos eles são muito bons. de qualquer maneira, quando você sh -c 'cat & kill $$'cria um processo em segundo plano e seu pai morre, você fica órfão e acaba sendo colhido eventualmente. esse é o trabalho do init - é por isso que todos caem nisso.
mikeserv
Talvez uma pergunta mais simples agora é: Como é que é possível obter a árvore do processo acima do shell: systemd--bash--chromium. Todos os métodos que eu tento acabarão levando a uma árvore de processos da seguinte forma systemd--chromiumquando eu gerar o Firefox do shell. Como a concha é demonizada aqui? Não está associado a nenhum terminal.
Lord.garbage 27/08

Respostas:

7

Bem, você parece ter um bom entendimento disso. Para esclarecer um pouco do que você tem,

  • sh -c /path/to/Program é bastante semelhante a

    $ sh 
    % / path / to / Program 
    % Ctrl+ D                             (ou você pode digitar “ exit ”) 
    $

    onde você inicia um novo processo de shell, forneça o caminho de comando do aplicativo para o novo shell e deixe o novo shell terminar. Eu mostrei o novo shell dando um prompt diferente para fins de ilustração; isso provavelmente não aconteceria na vida real. A construção é útil principalmente para fazer coisas complicadas, como agrupar vários comandos em um pacote, para que pareçam um único comando (tipo de script não nomeado de uso único) ou para criar comandos complicados, possivelmente a partir de variáveis ​​do shell. Você dificilmente o usaria apenas para executar um único programa com argumentos simples.sh -c "command"

  • 2>&1significa redirecionar o erro padrão para a saída padrão. Isso realmente não tem muito a ver com &; em vez disso, você o utiliza quando um comando envia mensagens de erro para a tela, mesmo que você diga e deseje capturar as mensagens de erro no arquivo.command > file
  • Redirecionar a saída para nohup.outé um efeito colateral trivial de nohup. O principal objetivo de é executar de forma assíncrona (geralmente conhecida como "em segundo plano" ou como "processo independente do shell", para usar suas palavras) e configurá-lo para que tenha uma melhor chance de poder continuar sendo executado se você encerre o shell (por exemplo, logout) enquanto o comando ainda estiver em execução.nohup command &command

bash(1)e o Manual de Referência do Bash são boas fontes de informação.

G-Man Diz 'Reinstate Monica'
fonte
7

Existem algumas maneiras de executar um programa e desconectá-lo de um terminal. Um deles é para executá-lo em segundo plano de um subnível , como este (substitua firefoxcom seu programa favorito):

(firefox &)

Outra é negar o processo:

firefox & disown firefox

Se você está curioso sobre como lançadores de aplicativos de trabalho, dmenufornece um binário e 2 scripts shell: dmenu, dmenu_pathe dmenu_run, respectivamente.

dmenu_runcanaliza a saída para dmenu_pathdentro do dmenu, que por sua vez canaliza para qualquer que seja sua $SHELLvariável definida. Se estiver vazio, ele será usado /bin/sh.

#!/bin/sh
dmenu_path | dmenu "$@" | ${SHELL:-"/bin/sh"} &

dmenu_pathé um pouco mais complexo, mas, em resumo, fornece uma lista de binários na $PATHvariável de ambiente e usa um cache, se possível.

#!/bin/sh
cachedir=${XDG_CACHE_HOME:-"$HOME/.cache"}
if [ -d "$cachedir" ]; then
        cache=$cachedir/dmenu_run
else
        cache=$HOME/.dmenu_cache # if no xdg dir, fall back to dotfile in ~
fi
IFS=:
if stest -dqr -n "$cache" $PATH; then
        stest -flx $PATH | sort -u | tee "$cache"
else
        cat "$cache"
fi

Não é necessário ter programas rodando em shell. Outra maneira de escrever dmenu_run, sem canalizar para um shell, seria:

#!/bin/sh
$(dmenu_path | dmenu "$@") &
svenper
fonte
6

Eu gosto muito da resposta do G-Man. Mas estou respondendo porque acho que você está confundindo preocupações. Como Wayne ressalta, a melhor resposta é "o que obtiver os resultados desejados".

No gerenciamento de processos Unix, todo processo tem um pai. A única exceção é o initprocesso iniciado pelo sistema operacional na inicialização. É um comportamento normal de um processo pai levar todos os seus processos filhos quando ele morre. Isso é feito enviando o sinal SIGHUP para todos os processos filhos; o tratamento padrão do SIGHUP encerra o processo.

A geração de shell dos processos do usuário não é diferente do que se você codificasse as chamadas fork (2) / exec (3) no idioma de sua escolha. O shell é seu pai e, se o shell terminar (por exemplo, você efetua logoff), o (s) processo (s) filho (s) gerado (s) o acompanha. As nuances que você descreve são apenas maneiras de modificar esse comportamento.

exec /path/to/programé como chamar exec (3) . Sim, ele substituirá o seu shell program, mantendo o pai que o lançou.

sh -c /path/to/programmeio que inutilmente cria um processo de shell filho que criará um processo filho de program. Só é valioso se /path/to/programfor realmente uma sequência de instruções de script, e não um arquivo executável. ( sh /path/to/script.shpode ser usado para executar um script de shell que não possui permissões de execução em um shell inferior)

/path/to/programcria um processo de "primeiro plano", o que significa que o shell aguarda a conclusão do processo antes de executar qualquer outra ação. No contexto de chamada do sistema, é como fork (2) / exec (3) / waitpid (2) . Observe que o filho herda stdin / stdout / stderr do pai.

/path/to/program &(ignorando o redirecionamento) cria um "processo em segundo plano". O processo ainda é filho do shell, mas o pai não está aguardando o término.

nohup /path/to/programchama nohup (1) para impedir o envio de SIGHUP para programse o terminal de controle estiver fechado. Seja em primeiro plano ou em segundo plano, é uma escolha (embora o processo seja mais comum em segundo plano). Observe que nohup.outé apenas a saída se você não redirecionar o stdout.

Quando você coloca um processo em segundo plano, se o processo pai morre, uma das duas coisas acontece. Se o pai for um terminal de controle , o SIGHUP será enviado aos filhos. Caso contrário, o processo pode ser "órfão" e é herdado pelo initprocesso.

Ao redirecionar a entrada / saída / erro, você está simplesmente conectando os descritores de arquivo que todo processo possui a arquivos diferentes daqueles herdados de seu pai. Nada disso afeta a propriedade do processo ou a profundidade da árvore (mas sempre faz sentido redirecionar todos os 3 para longe de um terminal para processos em segundo plano).

Com tudo isso dito, não acho que você deva se preocupar com a criação de processos pelo shell ou subconchas ou subprocessos, a menos que exista um problema específico que você esteja abordando relacionado ao gerenciamento de processos.

jwm
fonte
nitpick: sh -c /path/to/programnão executará o programa como um shell script se estiver faltando os bits executáveis sh /path/to/program. sh -c /path/to/programapenas abrirá um shell e executará /path/to/programcomo um comando nesse shell, que falhará se não for executável.
Filbranden
Bem, se estivermos detalhando, estamos ambos errados. sh -c /path/to/programlê comandos de /path/to/programcomo entrada para o shell. Ele não requer que o arquivo tem permissão de execução, mas deve ser um script shell
jwm
Hmmm, sh /path/to/programfaz isso. Apenas tentei isso:, echo echo hello world >abc.shentão sh ./abc.shimprime hello world, enquanto sh -c ./abc.shdiz sh: ./abc.sh: Permission denied(que é o mesmo que se você executasse ./abc.sho shell atual diretamente.) Perdi alguma coisa? (Ou talvez eu não me expressei bem no comentário anterior ...)
filbranden
Minha culpa. sh -c _something_é o mesmo que digitar _something_no prompt de comando, exceto pela geração do shell inferior. Portanto, você está certo de que falhará se estiver faltando o bit de execução (como um arquivo). Por outro lado, você pode fornecer uma série de comandos do shell sh -c "echo hello world"e ele funcionará perfeitamente. Portanto, não é necessário que o que você digita tenha o bit de execução (ou até mesmo um arquivo), apenas que o interpretador de shell possa fazer algo com ele.
jwm
Acredito que o OP estava se referindo a algum compilado ou executável do sistema, portanto, a permissão de execução foi assumida.
jwm