シェルからアプリケーションを「正しく」起動する方法


21

質問を正確に表現するのは難しいと思いますが、最善を尽くします。dwmデフォルトのウィンドウマネージャーとして使用し、dmenu私のアプリケーションランチャーとして。ブラウザ以外のGUIアプリケーションはほとんど使用しません。私の作業のほとんどは、コマンドラインから直接行われます。さらに、私はオペレーティングシステム、アプリケーションなどに関するミニマリズムの大ファンです。私が取り除けなかったツールの1つは、アプリケーションランチャーでした。主に、アプリケーションランチャーの動作/動作について正確に理解していないためです。大規模なインターネット検索でも、あいまいな説明しか表示されません。私がやりたいのは、アプリケーションを実際に生成すること以外はまったく使用できないため、アプリケーションランチャーを削除することです。これを行うには、シェルからアプリケーションを「正しく」起動する方法を知りたいと思います。これにより、「正しく」の意味は「アプリケーションランチャーが行うように」に近似できます。

シェルからプロセスを生成する次の方法について知っています。

  1. exec /path/to/Program 新しいプロセスを作成せずに、指定されたコマンドでシェルを置き換えます
  2. sh -c /path/to/Program シェル依存プロセスを起動する
  3. /path/to/Program シェル依存プロセスを起動する
  4. /path/to/Program 2>&1 & シェルに依存しないプロセスを起動する
  5. nohup /path/to/Program & シェルに依存しないプロセスを起動し、出力を nohup.out

更新1:さまざまな条件下でのdmenu繰り返し呼び出しからの再構築などを説明できps -eflます。新しいシェル/bin/bashと、このシェルの子としてアプリケーションが生成されます/path/to/Program。子供が周りにいる限り、シェルは周りにあります。(これを管理する方法は私を超えています...)対照的にnohup /path/to/Program &、シェルから発行する場合/bin/bash、プログラムはこのシェルの子になりますが、このシェルを終了すると、プログラムの親が最上位のプロセスになります。したがって、最初のプロセスがたとえば/sbin/init verbose、それがあった場合、それはPPID 1プログラムの親になります。ここで私は、グラフを使って説明しようとしたものです:chromium経て発足したdmenufirefox使用して開始されました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

更新2:質問は次のように要約することもできると思います:プロセスの親はどうあるべきですか?たとえば、シェルであるか、initプロセス、つまりプロセスである必要がありますPID 1か?


3
あなたの質問に対する簡潔な答えは、「あなたが望む結果を得るものは何でも」でしょう。
ウェインワーナー14

1
ダン、ゴミ-あなたはいくつかの良い質問をします。しかし、ウェインはここにいると思います-あなたの最新の編集が尋ねるinit-答えは...多分ですか?どのように/どのように話したいか、何initを使用するか、データチャネルがどこにあるかによって異なります。一般に、そのようなものはうまくいく傾向があります-それinitが目的です。いずれにせよ、通常はプロセスをデーモン化するときにinit。または、ジョブ制御が必要な場合は、現在のシェル。
mikeserv 14

ハハハ、@ mikeservを応援します。午前4時37分、ここで午前中、すでにその日の最初の笑い声。確かに、それらは常に何らかの形で機能します。取り除いてdmenu、学んだこととどうやって調和するかを見ていきます。私は見つけるexec /path/to/Program & exit/bin/bash -c /path/to/Program & exit、非常に使いやすいです。しかし、それらはすべて、1つまりinit、その親になりProgram、これが理にかなっていて、基本*nix原則に違反しない限り、私にとっては問題ありません。
lord.garbage 14

@ lord.garbage-それはあなたexec &だからだと思う。私は通常、端末から自分のことをします...多分あなたはここでベン・クロウェルの質問からいくつかの使用を得るでしょう。答えはありますが、どれも非常に優れています。とにかく、プロセスをバックグラウンドにすると、その親が次のように死んだsh -c 'cat & kill $$'とき、あなたはそれを孤児にし、最終的に刈り取られます。それがinitの仕事です-だからこそ、彼らはそれに陥ります。
mikeserv 14

たぶん今のシンプルな質問は:どのようにそれはシェルから上記のプロセスツリーを取得することが可能です:systemd--bash--chromium。私が試みるすべてのメソッドsystemd--chromiumは、シェルからfirefoxをスポーンすると、最終的に次の形式のプロセスツリーにつながります。ここでシェルはどのように悪魔化されますか?どの端末にも関連付けられていません。
lord.garbage 14

回答:


7

さて、あなたはそれをかなりよく理解しているようです。あなたが持っているもののいくつかを明確にするために、

  • sh -c /path/to/Program にかなり似ています

    $ sh/ path / to / ProgramCtrl+ D                             (または「exit 」と入力できます 
    $

    新しいシェルプロセスを開始する場所、新しいシェルへのアプリケーションコマンドパスを指定してから、新しいシェルを終了させます。説明のために別のプロンプトを表示する新しいシェルを示しました。これはおそらく実生活では起こらないでしょう。構築物は、それらが単一のコマンド(シングルユース無名のスクリプトの一種)のように見えるので、バンドルに複数のコマンドをラップする、またはシェル変数から、おそらく、複雑なコマンドを構築するなど、トリッキーなものを行うため、主に便利です。単一のプログラムを単純な引数で実行するためだけに使用することはほとんどありません。sh -c "command"

  • 2>&1は、標準エラーを標準出力にリダイレクトすることを意味します。これは実際にはあまり関係ありません&。むしろ、たとえ ファイル内のエラーメッセージをキャプチャしたいと言っても、コマンドがエラーメッセージを画面に送信するときに使用します。command > file
  • への出力のリダイレクトnohup.outは、の些細な副作用ですnohup。主な目的 は、非同期で実行し(一般的に「バックグラウンドで」または「シェル独立プロセス」として知られ、言葉を使用する)、それを設定して、次の場合に実行を継続できるようにすることです。コマンドの実行中にシェルを終了します(ログアウトなど)。nohup command &command

bash(1)およびBashリファレンスマニュアル は、優れた情報源です。


7

プログラムを実行して端末から切り離す方法はいくつかあります。1つは、次のようにサブシェルのバックグラウンド実行することです(firefoxお気に入りのプログラムに置き換えてください):

(firefox &)

もう1つは、プロセスを否認することです。

firefox & disown firefox

アプリランチャーの動作に興味がある場合は、それぞれdmenu1つのバイナリスクリプトと2つのシェルスクリプトを提供します:dmenudmenu_pathおよびdmenu_run

dmenu_runの出力dmenu_pathをdmenuにパイプし、dmenuは$SHELL変数に設定されているものにパイプします。空の場合、を使用します/bin/sh

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

dmenu_path少し複雑ですが、要するに、$PATH環境変数のバイナリのリストを提供し、可能であればキャッシュを使用します。

#!/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

プログラムをシェルで実行する必要はありません。dmenu_runシェルにパイプすることなくを記述する別の方法は次のとおりです。

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

6

私はG-Manの答えがとても好きです。しかし、私はあなたが混乱している懸念だと思うので、私は応答しています。ウェインが指摘するように、最良の答えは「あなたが望む結果を得るものは何でも」です。

Unixプロセス管理では、すべてのプロセスに親があります。これの1つの例外は、initブート時にOSによって開始されるプロセスです。親プロセスが死んだときに、すべての子プロセスを一緒に使用するのは通常の動作です。これは、すべての子プロセスにSIGHUPシグナルを送信することにより行われます。SIGHUPのデフォルトの処理はプロセスを終了します。

ユーザープロセスのシェル生成は、選択した言語でfork(2) / exec(3)呼び出しをコーディングした場合と変わりません。シェルは親であり、シェルが終了した場合(たとえば、ログオフした場合)、それが生成する子プロセスはそれと一緒になります。説明するニュアンスは、その動作を変更する方法にすぎません。

exec /path/to/programexec(3)を呼び出すようなものです。はい、シェルをprogramに置き換え、親がシェルを起動したままにします。

sh -c /path/to/programの子プロセスを作成する子シェルプロセスを無意味に作成しますprogram/path/to/program実際にスクリプト命令のシーケンスであり、実行可能ファイルではない場合にのみ価値があります。(sh /path/to/script.sh下位のシェルで実行権限のないシェルスクリプトを実行するために使用できます)

/path/to/program「フォアグラウンド」プロセスを作成します。つまり、シェルはプロセスが完了するのを待ってから他のアクションを実行します。システムコールコンテキストでは、fork(2) / exec(3) / waitpid(2)のようなものです。子は親からstdin / stdout / stderrを継承することに注意してください。

/path/to/program &(リダイレクトを無視)は、「バックグラウンドプロセス」を作成します。プロセスはまだシェルの子ですが、親はプロセスの終了を待っていません。

nohup /path/to/programnohup(1)を呼び出してprogram、制御端末が閉じている場合にSIGHUPが送信されないようにします。フォアグラウンドであるかバックグラウンドであるかは選択です(ただし、ほとんどの場合、プロセスはバックグラウンドです)。nohup.out標準出力をリダイレクトしない場合の出力のみであることに注意してください。

バックグラウンドでプロセスを配置するときに、親プロセスが停止すると、次の2つのいずれかが発生します。親が制御端末である場合、SIGHUPは子に送信されます。そうでない場合、プロセスは「孤立」している可能性があり、initプロセスに継承されます。

入力/出力/エラーをリダイレクトするときは、すべてのプロセスが持つファイル記述子を、親から継承するファイルとは異なるファイルに接続するだけです。これは、プロセスの所有権やツリーの深さに影響しません(ただし、3つすべてをバックグラウンドプロセスのターミナルからリダイレクトすることは常に意味があります)。

とは言っても、プロセス管理に関連して対処している特定の問題がない限り、シェルまたはサブシェルまたはサブプロセスによるプロセスの作成を心配する必要はないと思います。


nitpick:sh -c /path/to/program実行可能ファイルが欠落している場合、プログラムをシェルスクリプトとして実行しませんsh /path/to/programsh -c /path/to/program単にシェルを開き、/path/to/programそのシェルでコマンドとして実行しますが、実行可能でない場合は失敗します。
フィルブランデン

まあ、私たちが盗み見している場合、私たちは両方とも間違っています。シェルへの入力としてsh -c /path/to/programコマンドを読み取ります/path/to/program。ファイルに実行権限がある必要はありませんが、シェルスクリプトである必要があります
-jwm

うーん、sh /path/to/programそれをします。:ただ自分がすることを試みたecho echo hello world >abc.sh場合、sh ./abc.shプリントがhello worldながら、sh -c ./abc.sh言うsh: ./abc.sh: Permission denied(あなたが実行したい場合と同じである./abc.sh。直接現在のシェルで)私が何かを見逃していましたか?(または、前のコメントで自分自身をうまく表現しなかったかもしれません...)
フィルブランデン

私のせい。下位シェルの生成を除いて、コマンドプロンプトでsh -c _something_入力_something_するだけと同じです。したがって、実行ビットが欠落していると(ファイルとして)失敗することは間違いありません。一方、次のような一連のシェルコマンドを提供することもできますsh -c "echo hello world"。したがって、入力するものに実行ビット(またはファイル)を含める必要はなく、シェルインタープリターがそれを使用して何かを行うことができます。
jwm

OPはコンパイル済みまたはシステム実行可能ファイルを参照していたため、実行権限が想定されていたと思います。
jwm
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.