🎪

TIL: UNIXプログラミング環境 第3章 前半

に公開

UNIXプログラミング環境 第3章 前半

内容はToday I Learnedということで、学びを記す記事です。
 詳しいわけではないので悪しからず。
書籍はこちら
UNIXプログラミング環境

第3章シェルの利用

シェル - ユーザのプログラム実行要求を解釈するプログラム

複雑な機能を扱い、シェルをつかいこなすための章。待ってました。

3.1 コマンド行の構造

コマンドとはそもそも何なのか。

理解するためにいくつかコマンドを実行する。

$ date ; who | wc
20252月27日 木曜日 20時50分17秒 JST --- date の結果
       2      10      88            --- who | wc の結果

dateとwhoが;で区切られているため、別々の行で2行書いた時と同じ結果になる。
しかし上のようにパイプでwcに渡そうとすると、whoの実行結果のみwcされていることがわかる。

これは |;より優先順位が高く、;でコマンドが途切れていて、後ろのwho | wcがパイプラインというまとまったコマンドになるためだそうです。

datewhoの結果をまとめてwcしたい場合は、()で囲むことで実現できる。

$ (date; who) | wc
       3      15     136

この場合は()が優先され、dateとwhoの出力は連結され、まとめてパイプに送り込むことができる。

途中結果を保存したければ tee (ファイル名)コマンドが便利。

パイプの中に流れていくデータを捉えられる。

$ (date;who) | tee save | wc
       3      15     136
$ cat save
20252月27日 木曜日 20時58分12秒 JST
(本名だよ)     console       1  9 07:10  
(本名だよ)     ttys001       2 27 20:49

またコマンドの終了を意味するのは;以外に&がある。

これは1章でも軽く触れられていたが、バックグラウンド実行が可能になるコマンドである。
これと()を使うと、次のようなことが起こる。

$ (sleep 5; date) & date
[1] 44629
20252月27日 木曜日 21時01分38秒 JST -- &以降のdateの結果
$ 20252月27日 木曜日 21時01分43秒 JST -- ()内のdateの結果

sleep は任意の秒数実行を待つコマンドである。
(sleep 5; date)が実行され5秒待っている間に、&によってバックグラウンド実行へ周り、その間に二個目のdateが実行されることで、上記のような挙動になっている。

シェルによって解釈される特殊記号はシェルが走らせているプログラムに対して与えられる引数ではない、あくまで制御のために使用される。
そのため、

$ echo hello >junk

というコマンドを考えた時、echo からjunkは見えない。これを利用するとこのコマンドは

$ >junk echo hello

としても実行できる。(可読性がよくないので非推奨!)

  • 最も単純なコマンドは1ワードでできている。
    • e.g. who ,ls ,date

3.2 メタキャラクタ

*|>といった特別な制御文字のことをメタキャラクタという。

シェルのメタキャラクタはかなり多いので、都度勉強する方が良さそう。

代表的なものと便利そうなものを以下に示す。

メタキャラクタ 説明
> program > file でprogramの結果の標準出力をfileに切り替える
>> program >> file でprogramの結果をfileに追加する
< program < file でファイル内容を標準入力としてfileを読み込む
| p1 &#124; p2 でp1の標準出力をp2の標準入力にする
* 0文字以上の任意の文字列と一致する
? ファイル名中の任意の一文字と一致する
; p1 ; p2でp1実行後にp2を実行
& p1 & p2でp1の終了を待たずにp2を実行
(. . .) ()の中にあるコマンドをサブシェルで実行する

制御文字を制御文字ではなくただの記号として使いたい場合は、もっとも簡単な方法として 'でくくってしまうやり方がある。

*を出力したくても、↓だと lsとのような動きをしてしまう。

$ echo *
cryptedtext junk linktojunk recipes save (ファイル等)

*を出力したい場合はこう。

$ echo '*'
*

UNIXのおとぎ話(?)やメタキャラクタの細かい解説がなされていました。
実務よりというより設計思想の話だったので省きますが、面白かったので一読をお勧めします!

3.3 新しいコマンドの作成法

"自分で作る"
エンジニアの一番好きな言葉で他のエンジニアにもっとも嫌われる所以ですね。

今回は簡単な例をそのまま紹介します。

$ who | wc -l

whoの実行結果の行数をカウントするシェルスクリプトです。
これを一つのnuコマンドとして作成します。

$ echo 'who | wc -l' > nu

nuというファイルにwho | wc -lが入りました。
あとはシェルスクリプトを実行するコマンド shに入力するだけです。

$ sh <nu
2

shすらいらないようにしたい!といいたいのですが、自作コマンドに関しては他の方の記事を参考にするのが良いと思います。
以下のようなステップが必要です。

  1. コマンドを置くディレクトリを作成する
  2. パスを通す
  3. コマンドを作る
  4. 実行権限を付与する

自作コマンド シェル などで検索すると出てくると思います。

3.4 コマンドの引数、パラメータ

コマンドの引数は $n (n > 1以上の自然数)で複数定義できる。

一つの引数を受けるcxコマンドを作成してみる。

$ echo 'chmod +x $1' > cx

受け取った引数ファイルに実行権限を追加するコマンドである。
先ほどと同様にshで適当なファイルを作成、指定して実行すると

$ echo 'test' > test
$ ls -l
-rwxr-xr--  1 (本名だよ)  staff   15  3 16 21:48 test (実行権限がない)
$ sh cx test
$ ls -l
-rwxr-xr-x  1 (本名だよ)  staff   15  3 16 21:48 test (実行権限が付いている!)

引数を複数使いたい場合は $1,$2,3と増やしていけば良いのだが、無数に受け付ける場合は `*`と *を使用できる。

$ echo 'chmod +x $*' > cx
$ echo 'test' > test
$ echo 'test' > test1
$ echo 'test' > test2
$ echo 'test' > test3
$ ls -l
-rw-r--r--  1 (本名だよ)  staff    5  3 16 21:54 test
-rw-r--r--  1 (本名だよ)  staff    5  3 16 21:54 test1
-rw-r--r--  1 (本名だよ)  staff    5  3 16 21:54 test2
-rw-r--r--  1 (本名だよ)  staff    5  3 16 21:54 test3
$ sh cx test,test1,test2,test3
-rw-r--r-x  1 (本名だよ)  staff    5  3 16 21:54 test
-rw-r--r-x  1 (本名だよ)  staff    5  3 16 21:54 test1
-rw-r--r-x  1 (本名だよ)  staff    5  3 16 21:54 test2
-rw-r--r-x  1 (本名だよ)  staff    5  3 16 21:54 test3

拡張性がいいですね。

空白を含めて一つの引数にしたい場合は、以下のようにする。

$ echo 'grep "$*" /(電話帳の役割を持つファイルを指定)' > 電話帳から名前で検索可できるコマンド

引数を""で囲っていないと、

$ 電話帳から名前で検索可できるコマンド 山田 太郎

と山田太郎さんを検索したくても、

$ grep 山田 太郎 /(電話帳の役割を持つファイルを指定)

とgrepに複数の引数を渡してしまい、挙動がおかしくなる。

""で囲むことで、

$ grep "山田 太郎" /(電話帳の役割を持つファイルを指定)

のように動くコマンドが作成できるのだ!

まとめ

年度末 + 風邪をひいてしまったせいで少し勉強時間が確保できていませんでした。
これからも引き続き頑張る予定です。

  • コマンドは作れる!
  • メタキャラクタは単純ゆえに奥深い
  • 実行時間でかなり間が空いているのがバレる。

今回は自作でコマンドを作るというワクワクする内容で途中からはノリノリでした。
勉強するっていいですね。楽しい内容だけで良いならいいんですが苦しい時が来るまでは楽しみます〜

Discussion