※当サイトの記事には、広告・プロモーションが含まれます。

シェルスクリプトは基本、逐次実行らしいが、前の処理の終了を待たないケースもあるという罠

japan.cnet.com

 Microsoftは、690億ドル(約10兆円)でのゲームパブリッシャーActivision Blizzard買収に一歩近づいたようだ。

マイクロソフトによる10兆円規模のActivision買収、英当局が承認を示唆 - CNET Japan

⇧ 金額がエグい...

www.itmedia.co.jp

⇧ 何て言うか、ブラックボックスありきということを承知で進めてきてるわけだから、制御できなくなるリスクが顕在化する時が来るかもしれないって話は今に始まったことではないと思うけどね...

逐次と順次の違いは?

シェルスクリプトについて調べていて、

xtech.nikkei.com

 プログラムをコンピュータ上で実行するには,「ソース・プログラム*マシン語機械語)に一括して翻訳してから実行する方式」と,「ソース・プログラムを逐次解釈して実行する方式」があります。前者をコンパイラ*方式,後者をインタプリタ方式といい,シェルは後者の方式になります。

シェル・スクリプト(その1:シェル・スクリプトとは) | 日経クロステック(xTECH)

sleepy-yoshi.hatenablog.com

というわけで,タイトルにある通りシェルスクリプトは文字通りスクリプトを一行ずつ読み込んで逐次処理している.

シェルスクリプトは文字通りの逐次実行 - シリコンの谷のゾンビ

⇧ 結構な頻度で「逐次」という言葉が出てきますと。

「構造化プログラミング」のWikipediaのページで、

構造化プログラミング - Wikipedia

⇧「命令型プログラミング」の内の1つという扱いらしい。

「構造化プログラミング」の説明で、

制御構文

コード群とは命令コード(instruction code)のまとまりであり、構造化定理では部分プログラム(subprogram)と定義されている。部分プログラムはステートメント(statement)コードブロック(code block)サブルーチン(subroutine)の総称である。ステートメントは命令コードの一行を意味する。

構造化プログラミング - Wikipedia

コードブロックは一行以上のステートメントをまとめたものである。サブルーチンは一行以上のステートメントまたは一個以上のコードブロックを内包している。部分プログラムは直列状または入れ子状に配置される。その実行順序を決定するものが制御構文であり、以下の三つがある。

  1. 順次(sequence)部分プログラムを順々に実行する。
  2. 選択(selection)条件式が導出した状態に従い、次に実行する部分プログラムを選択して分岐する。
  3. 反復(repetition)条件式が導出した特定の状態の間、部分プログラムを繰り返し実行する。

構造化プログラミング - Wikipedia

⇧ プログラミングのロジックを考える上で基本となる「順次」「選択」「反復」の話が出てきますと。

で、気になったのが、

  • 逐次
  • 順次

の違いってのをハッキリさせずに何となく曖昧な理解でこれまで生きてきたなと。

Googleで検索して、『Oxford Languages and Google - Japanese』の解釈によると、

■逐次

■順次

という結果になりましたと。

  • 逐次
    順序を追って次々に。順次。
  • 順次
    次から次へ順を追ってすること。順繰り。順々。

⇧「逐次」の説明で「順次」って出てくるってことは、同じ意味と理解して良いのだろうか?

ただ、計算科学(コンピューター)の分野においても、同じような解釈をして良いものかが分からんのだけど...

とりあえず、モヤモヤするけども、計算科学(コンピューター)の分野においても、「逐次」と「順次」は同じ意味ということで話を進めることにします。

シェルスクリプトとは?

Wikipediaさんに聞いてみた。

シェルスクリプト (英語shell script) は主にオペレーティングシステムシェルまたはコマンドラインインタプリタから実行可能なコマンドの一連の流れをファイルにして再利用できるようにしたものである。狭義では、Unixシェルで用いられるスクリプト言語を指す。

シェルスクリプト - Wikipedia

プログラミング

現代の多くのシェルは手続き型プログラミング言語にある基本的な制御フロー構造などに似た豊富な機能を備えている。制御構造、変数、コメント、配列、サブルーチンなどである。それらを使えばかなり洗練されたアプリケーションをシェルスクリプトで書くことも可能である。しかし、シェル言語の多くはデータ型システム、クラス、スレッド、複雑な数学的計算といった高水準言語に見られる機能をほとんどサポートしていない。また、コンパイラや性能重視のインタプリタで実装された汎用言語に比べれば実行速度が遅い。

シェルスクリプト - Wikipedia

⇧ 手続き型プログラミング寄りってことなんかね?

「手続き型プログラミング」はと言うと、Wikipediaの「構造化プログラミング」の説明で「命令型プログラミング」に含まれるらしい。

まぁ、ただ、「シェルスクリプト」の説明がハッキリしないボヤかした感じになってるので、モヤモヤするのだけど...

で、「シェルスクリプト」の話に戻ると、

長所と短所

同じプログラムを書く場合、他のプログラミング言語よりもシェルスクリプトの方が短く書けることが多い。OS標準コマンド以外を含む豊富なコマンドを容易に利用できるからである。既存のプログラム群を順次実行したり、コマンドの実行結果やファイルの有無などの条件判断を伴って実行する場合などはシェルスクリプトが有効で、限定的ながらパイプライン処理、バックグラウンドジョブ、xargsコマンドの-Pオプションなどを利用すれば、並列処理並行計算を容易に行うことができる。また、スクリプト言語全般に当てはまることではあるがコンパイルが不要という点も利点の一つである。

シェルスクリプト - Wikipedia

⇧ らしい。

なるほど、順次実行以外になるケースもあるということでしたか...。

シェルスクリプトって勝手に上から順番に実行されていくイメージがあったんだけど、知らず知らずのうちに非同期処理になってしまっているということもありえるってことなんですな。

シェルスクリプトのバックグラウンドジョブとは

どうやら、

lecture.ecc.u-tokyo.ac.jp

5.1.5.2. バックグラウンド処理/Running in background

処理に時間がかかるプログラムをバックグラウンドで実行させることができる。 そのためには、コマンドラインの最後に「&」をつける。

5.1.5. シェルのジョブ制御/Shell job control — Simulation programming guidebook for C++ 1.0 documentation

例えば、長時間かかることが分かっているmakeを実行して、メッセージは全てファイルに 残しておくのであれば、次のように入力する:

make >& make.log &

行末の「&」がバックグラウンド実行の指定である。

5.1.5. シェルのジョブ制御/Shell job control — Simulation programming guidebook for C++ 1.0 documentation

「&」を付けるとバックグラウンドジョブになるらしい。

バックグラウンドジョブになると、

jehupc.exblog.jp

;(セミコロン)
前のコマンドが終わり次第、次のコマンドが実行される。(3つ以上のコマンドを連結してOKみたい)

(Linux)コマンドを連結して行う「;」「&」「&&」「||」の違い : old_3流プログラマのメモ書き

&(アンパサンド)
;(セミコロン)は前のコマンドの完了を待っていたが、&は待たない。

(Linux)コマンドを連結して行う「;」「&」「&&」「||」の違い : old_3流プログラマのメモ書き

&&
前のコマンドがうまく終了した(終了ステータスが0)なら、次のコマンドを実行。そうでないなら次のコマンドは実行しない。

(Linux)コマンドを連結して行う「;」「&」「&&」「||」の違い : old_3流プログラマのメモ書き

||
&&とは逆で前のコマンドに失敗(終了コード0以外)したら、次のコマンドを実行する。

(Linux)コマンドを連結して行う「;」「&」「&&」「||」の違い : old_3流プログラマのメモ書き

⇧ 前の処理の完了を待たないらしい。

でも、処理時間とか計測したい場合に前の処理を待ちたいことってあると思うんですよね。

そんな時は、

dorapon2000.hatenablog.com

&はシェルにおいてバックグラウンド実行をするコマンド?です。 そして、複数のバックグランドジョブの終了を待つコマンドがwaitです。

シェルの&とwaitについて少し調べてみた - dorapon2000’s diary

⇧ とあるように、「wait」を使えばバックグラウンドジョブの処理が完了するまで待機させてくれるらしい。

複数のバックグラウンドジョブを直列実行したい場合は、

qiita.com

バックグラウンドプロセスを用いて直列実行がしたい場合

echo "hogehoge.shを実行します"
./hogehoge.sh & # バックグラウンドプロセスとして起動
wait $! # $!で直前のバックグラウンドのプロセスIDが得られるため、それが終わるまで待機
echo $? # hogehoge.shの終了ステータス
echo "fugafuga.shを実行します"
./fugafuga.sh & # hogehoge.shが終了してから実行されるバックグラウンドプロセス
wait $!
echo $? # fugafuga.shの終了ステータス

そもそもバックグラウンドプロセスにしている時点で直列実行する必要がないだろうが、上記のようにプロセスIDを$!で取得し指定すれば可能

シェルスクリプトで単純に並列実行・直列実行を行う - Qiita

⇧ 上記サイト様が説明してくれていました。

パイプで複数コマンドが繋がってる時にバックグラウンドジョブを実施してる場合はどうしたら良いかと言うと、

qiita.com

⇧ かなり、と言うか、パイプが無くなってるんだが...

ちなみに、javaコマンドでクラスファイルをバックグラウンドジョブとして実行して、そのPIDを参照してjstatコマンドをフォアグラウンドで実行してる場合は、jstatコマンドはjavaコマンドで実行されてるプロセスの処理が完了するまで処理し続けるので、結果的にwait無しでも処理完了を待ってくれるというトリッキーな挙動を、現場で学びました。

まぁ、上記のケースだとjstatの処理の時間の分も計測に含まれてしまうので、java実行のみの正確な計測時間にはならないので、厳密に時間を計測するには他の方法を取る必要があるのだけど...

それにしても、シェルスクリプト、相変わらず書き方がよく分からん...

とりあえず、「逐次実行」は、順々に実行された処理について、処理が完了するのも実行された処理の順番通りになるとは限らないということに注意が必要ということでしょうかね。

バックグラウンドジョブを含まない直列実行であれば、処理の実行開始と実行完了も順番通りになると思うけど...つまり、前に実行された処理が完了してから次の処理が実行されて開始するってことかと。

毎度モヤモヤ感が半端ない...

今回はこのへんで。