コンテンツにスキップ

3章 シェルとスクリプト

用語

ターミナル

  • 文字の入出力
  • エスケープシーケンス
    • バックスベースとか
  • 種類
    • xterm, rxvt, Gnome terminator
    • New: Alacritty, kitty, warp GPUを利用しているらしい

シェル

ターミナル内で動作するプログラム。ストリームによる入出力処理、コマンドの実行をする。対話的/スクリプトでの使用。

user@vm:~$ file -h /bin/sh
/bin/sh: symbolic link to dash
user@vm:~$ echo $0
-bash
user@vm:~$ echo $SHELL
/bin/bash

ストリーム

入力ストリーム(input stream)と出力ストリーム(output stream)がある。2つ合わせて入出力(I/O, Input/Output)という。

シェルはすべてのプロセスに3つのファイルディスクリプタ(FD)をデフォルトで用意している。

  • stdin (FD 0): 標準入力
  • stdout (FD 1): 標準出力
  • stderr (FD 2): 標準エラー出力

特に指定しない限り、シェルで入力したコマンドはキーボードから入力(stdin)され、画面に出力(stdout)する。エラー発生時も画面に出力する(stderr)。

ストリームはリダイレクト1できる。$FD><$FDを使用する。$FDはファイルディスクリプタである。

例えば次の通り。

  • 2> stderrストリームをリダイレクト
  • 1>, > (同じ意味) stdoutストリームをリダイレクト
  • &> stderrstdoutの両方をリダイレクト

また、行き先を/dev/nullにするとストリームの出力は破棄される。

シェルは次の特殊文字を解釈する。

  • アンパサンド (&) コマンドの最後に置くとコマンドをバックグランドで実行する
  • バックスラッシュ (\) 次の行にコマンドを続ける
  • パイプ (|) あるプロセスのstdoutと次のプロセスのstdinをつなぐ

Info

  • curl HTTPリクエストを投げる
  • head 先頭から数行表示する
    • ex $ head -n 10
  • tr stdinから読み込んだ文字を置換する
  • wc 'word count' 文字や行数を数える

変数

2種類ある。unsetで未定義の状態に戻せる。

  • 環境変数
    • シェル全体の設定
    • exportを使って作成(bash)
  • シェル変数
    • 実行中のシェルでのみ有効、子プロセスには継承されない
    • setを使って作成もできる

変数にアクセスするためには変数の前に$をつける。 実例は次のとおり。

$ MY_VAR=42 # シェル変数
$ set | grep "^MY_*"
MY_VAR=42
$ echo MY_VAR
MY_VAR
$ echo $MY_VAR
42
$ export MY_GLB="omg" # 環境変数
$ set | grep "^MY_*"
MY_GLB=omg
MY_VAR=42
$ bash # ここから子プロセスのシェル
$ set | grep "^MY_*"
MY_GLB=omg # <- 環境変数だけ

次のような環境変数もある。シェルや環境によって異なる。

$ echo $? # ? 直前に実行したコマンドの実行ステータス (0が正常, 1-255は失敗)
0
$ echo $$ # $ 現在のプロセスのPID
198
$ echo $0 # 0 現在のプロセスの名前
-bash
$ ps
  PID TTY          TIME CMD
  198 pts/2    00:00:00 bash
  253 pts/2    00:00:00 ps
$ echo $_ # _ 直前のコマンド
ps

ビルドインコマンド

シェルにはビルドインコマンドが用意されている。そもそもコマンドにはシェルビルドインだけでなく、/usr/binなどシェルの外部にインストールやエイリアスのコマンドなど種類がある。

コマンドの種類を調べるためにはtypeが利用できる。

$ type cd
cd is a shell builtin
$ type sh
sh is /usr/bin/sh
$ type ls
ls is aliased to `ls --color=auto'

また、コマンドの実行可能ファイルがある場所は~~which~~2 command -v で取得できる。

参考 Qiita - POSIXコマンドは「どの環境にもあるコマンド」ではないよという話

ジョブ制御

フォアグラウンド実行 シェルで通常通りコマンドを実行し、画面とキーボードの制御を受ける

対話的に実行しない場合やサーバのようなプログラムの場合、ジョブ制御バッググラウンドジョブがある。

  • バックグラウンドでプロセスを起動するには最後に & をつける
  • フォアグラウンドのプロセスをバッググラウンドに送るには Ctrl + Z を押す
$ watch -n 5 "ls" & # watch コマンドをバッググラウンドで実行
[1] 299
$ jobs # すべてのジョブの一覧を表示
[1]+  Stopped                 watch -n 5 "ls"
$ fg # あるプロセスをフォアグラウンドで実行

Info

  • nohup COMMAND & シェルが閉じた後もコマンドをバックグラウンドで実行する
  • disown
  • kill 対象のプロセスにシグナルを送信して終了させる

モダンなコマンド

日常的に使うコマンドをより使いやすくしたコマンドがある。

  • exa ディレクトリの内容を出力
  • bat ファイルの内容を表示(catの進化系)
  • rg ファイルの中身検索(findgrep)
  • jq JSONデータの処理

シェル作業の効率化

ファイル内容の整理

viを使わないテキストの処理も色々ある。

# /tmp/something を作成し一行追加
$ echo "First line" > /tmp/something

# /tmp/something を表示
$ cat /tmp/something
First Line

# /tmp/something の2行目を追記
$ echo "Second Line" >> /tmp/something && \
> cat /tmp/something
First Line
Second Line

# /tmp/something の内容をsedで置換しstdoutへ (-iオプションで上書き)
$ sed 's/Line/LINE/' /tmp/something
First LINE
Second LINE


# ヒアドキュメントでファイルを作成
# https://tldp.org/LDP/abs/html/here-docs.html
$ cat << 'EOF' > /tmp/another
First Line
Second line
Third line
EOF

# ファイルの差分を表示
$ diff -y /tmp/something /tmp/another
First Line                                                      First Line
Second Line                                                   | Second line
                                                              > Third line

長いファイルの参照

lessbatのようなページャを使用するのが普通だが、他にもいくつか方法がある。

  • head ファイルの先頭行を表示
    • ex. head -5 [file] fileの5行目まで表示
  • tail -f 最終行から数行表示するコマンド + -f ライブアップデート出力
    • ex. tail -f [file]

  1. stdin, stdout, stderrをシェルのデフォルト以外に転送すること(シェルのデフォルトはキーボードやモニタ) 

  2. whichコマンドはPOSIXで規定されていない外部プログラムのため、利用できない環境もあるかもしれません。一方、commandはbashではビルドインコマンド(POSIXでも規定)です。