初心者向けに書かれているので、読みやすかった。
コマンドがファイルとして定義されていることすら知らないレベルだったので、勉強になった。
分かっているようで分かっていなかった「パスを通す」の意味も分かったし。分かってしまえば、なんで分からなかったんだろうという感じではあるが。
シェルスクリプトで何が出来るのか分かっていないと自動化の発想すら出てこないので、基礎が分かってよかった。
以下、各章のメモ。
第1章 楽しく学ぶシェルの基本
例外もあるが、コマンドというのは、基本的にはファイル。
which
コマンドを使うと、コマンドのファイルがどこにあるか調べることが出来る。
$ which date /bin/date $ which git /usr/local/bin/git $ which node /Users/$USER/.nodebrew/current/bin/node
標準出力、標準入力、標準エラー出力の3つは「標準ストリーム」と呼ばれる。
パイプ|
は、左側のコマンドの標準出力を、右側のコマンドの標準入力にする。
リダイレクト>
は、左側に書いたコマンドの標準出力先を、右側に書いたファイルにする。
>&2
は、標準出力ではなく標準エラー出力を使うようにする命令。
;
でコマンドをつなげることで、複数のコマンドをまとめて実行できる。これを「リスト」という。
$ echo a; echo b a b
「リスト」をまとめてパイプに渡すには、括弧で囲む。この記法を「サブシェル」という。
$ echo a; echo b | echo-sd -s a _人人_ > b <  ̄Y^Y^ ̄
$ (echo a; echo b) | echo-sd -s _人人_ > a < > b <  ̄Y^Y^ ̄
ちなみにecho-sd
コマンドは以下の手順で利用できるようになる。
$ curl -OL https://git.io/echo-sd $ sudo install -m 0755 echo-sd /usr/local/bin/echo-sd
第2章 テキスト処理で役立つシェル・テクニック
筆者の実際の原稿を題材に、テキスト処理を行う。
原稿は以下のリポジトリで公開されている。
grep
コマンドで検索できる。wc
コマンドは、入力された文字列の情報を返す。
以下は、カレントディレクトリ以下にある全ての.rst
ファイルを対象にできる
を検索し、該当した行数、単語数、バイト数、を表示している。
$ grep できる *.rst | wc 87 150 8572
wc
に-l
オプションを渡すと、行数のみが表示される。
$ grep できる *.rst | wc -l 87
awk
のFNR
は、各ファイルの行数を持っている。
$ awk 'FNR==2' *.rst 開眼 シェルスクリプト 第1回 開眼シェルスクリプト 第2回 開眼シェルスクリプト 第3回 開眼シェルスクリプト 第4回 ...
第3章 基本から押さえるシェルスクリプト
シェルスクリプトとは、シェルで実行するコマンドを順番に記述したテキストファイルのこと。
シェルスクリプトを用意することで、複雑なコマンドを簡単に実行できるようになる。
$ bash hoge.bash
のようにシェル(この例ではbash
)の引数にシェルスクリプトを渡すと、そのシェルスクリプトが実行される。
$ chmod +x hoge.bash
でシェルスクリプトに実行権限を付与できる。これで、$ ./hoge.bash
のようにシェルスクリプトを実行できるようになる。
./
のようなディレクトリの指定をせずにシェルスクリプトを実行するには、そのシェルスクリプトをコマンド検索パスに含める必要がある。
コマンド検索パスはPATH
という環境変数に入っているので$ echo $PATH
で見れる。
ディレクトリを指定せずにシェルスクリプトを実行すると、このコマンド検索パスの中から探す。そのため、hoge.bash
がここに含まれていなければ、$ hoge.bash
としたときにcommand not found
になってしまう。
$HOME/bin/
をPATH
に含めたい場合は、$ PATH="${PATH}:${HOME}/bin/
とする。
この状態でhoge.bash
を$HOME/bin/
に入れておくと、$ hoge.bash
を実行できる。
だがコマンド検索パスはログインする度に設定しないといけないので、.bash_profile
に書くなどしてログイン時に自動的に反映されるようにしておくとよい。
位置変数とは、シェルスクリプトの実行時に渡した引数が格納されている変数。
$1
や$2
のように、$
と引数の順番の組み合わせになっている。
hoge.bash
の内容が以下のときに$ hoge.bash abc xyz
とすると、abc
とxyz
が表示される。
echo $1 echo $2
コマンドは、実行結果を示す「終了ステータス」を返す。
コマンドが成功した場合は0
を、エラーが発生した場合は0
以外を返す。
シェルスクリプトのなかでexit
コマンドを使うと、その引数を終了ステータスとして返し、コマンドが終了する。
以下のように書くと、a
とb
を表示したあと、終了ステータスとして0
を返す。
exit
でコマンドを終了するためc
は表示されない。
echo a echo b exit 0 echo c
直前に実行したコマンドの終了ステータスは$?
という変数に入っている。
$ foo.bash -bash: foo.bash: コマンドが見つかりません $ echo $? 127
if
やfor
などの制御構文も扱える。条件式にはtest
コマンドを使うことが多い。
unix時間は$ date "+%s"
で取得できる。
この特集では特に触れられていなかった(サンプルには記述されていた)が、シェルスクリプトの1行目には#!/bin/bash
などのシバンを書いたほうがいいはず。
#!/bin/sh は ただのコメントじゃないよ! Shebangだよ! - Qiita
第4章 効率的で安全なファイル操作の秘訣
set -e
は、エラーが発生したらすぐにシェルスクリプトを終了させる。
set -u
は、未定義の変数を使った場合はエラーにする。
この2つを組み合わせたset -eu
をシェルスクリプトの冒頭に書いておくことで、記述ミスした場合はすぐにエラーになり、シェルスクリプトが意図せぬ操作を行ってしまうことを防げる可能性が高まる。
以下のコマンドで、パス直下のディレクトリやファイルをファイルサイズの順に並べて表示できる。
$ du -s パス | sort -nr | awk '{print $NF}' | xargs du -sh
ファイル操作は安全が第一なので、削除や上書きは自動化しない。
Appendix 1 シェルの「展開」をマスターしよう
bash
には「展開」という機能がある。*
も展開の一種で、「パス名展開」という。
ブレース展開
{
と}
でくくられた文字を展開する。区切り文字として,
か..
を用いる。
$ echo a{x,y}b axb ayb $ echo a{1..3}b a1b a2b a3b
x..y..z
とすると、x
から始まりy
に到達するまで、x
ずつインクリメントしていく。
$ echo a{1..8..3}b a1b a4b a7b
チルダ展開
~
の後ろにつけた文字列をユーザー名と解釈し、そのユーザーのホームディレクトリパスを展開する。
~
だけの場合は、実行したユーザーのホームディレクトリパスを展開する。
$ cd ~Guest $ pwd /Users/Guest
パラメータと変数展開
先頭に$
をつけると、変数を展開できる。
$ hoge=HOGE $ echo $hoge HOGE
コマンド置換
$(コマンド)
とすると、コマンドの実行結果を展開する。
$ echo 今日の日付は $(date '+%Y%m%d') です 今日の日付は 20190715 です
算術式展開
$((計算式))
とすると、計算式の結果を展開する。
$ echo 10 / 2 の結果は $((10/2)) です 10 / 2 の結果は 5 です
単語の分割
展開された文字列を、改行や空白を区切り文字にして分割する。
パス名展開
*
、?
、[
、]
をパターンとして認識しマッチしたファイルを展開する。
*
- 任意の文字列にマッチ
?
- 任意の1文字にマッチ
[]
- 間に挟まれた任意の1文字にマッチ
$ ls *.txt work_1.txt work_10.txt work_2.txt work_3.txt work_4.txt work_5.txt $ ls work_?.txt work_1.txt work_2.txt work_3.txt work_4.txt work_5.txt $ ls work_[13].txt work_1.txt work_3.txt
プロセス置換
コマンドの出力結果を、別のコマンドの引数として扱える。
<(コマンド)
を入力として、>(コマンド)
を出力として、扱う。
$ diff <(echo -e 'a\nb\nc') <(echo -e 'a\nb\nz') -u --- /dev/fd/63 2019-07-15 20:47:14.000000000 +0900 +++ /dev/fd/62 2019-07-15 20:47:14.000000000 +0900 @@ -1,3 +1,3 @@ a b -c +z
Appendix 2 シェルのカスタマイズ方法
プロンプトは環境変数PS1
で設定されている。
$ echo $PS1 \h:\W \u\$
noclobber
noclobber
オプションを有効にすると、>
によるファイルの上書きを禁止できる。
有効になっているかは、$ set -o | grep noclobber
で確認できる。
$ set -C
で有効にできる。
$ set -o | grep noclobber noclobber off $ set -C $ set -o | grep noclobber noclobber on $ echo hoge > foo.txt $ echo fuga > foo.txt -bash: foo.txt: 存在するファイルを上書きできません
nounset
未定義の変数を参照すると、空文字が返ってくる。
$ echo a${FOO}b ab
nounset
オプションを有効にすると、未定義の変数を参照した際にエラーが発生するようになる。
$ set -u
で有効になる。
$ set -o | grep nounset nounset off $ set -u $ set -o | grep nounset nounset on $ echo a${FOO}b -bash: FOO: 未割り当ての変数です