1. ryuichi1208

    Posted

    ryuichi1208
Changes in title
+bashで忘れがちな機能とかいろいろの備忘録
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,312 @@
+![Bash-Final.jpg](https://qiita-image-store.s3.amazonaws.com/0/258841/efb5999f-66ac-ed91-08d6-fc5efe4b8f25.jpeg)
+
+## 背景
+
+シェルスクリプトを書くことが結構あるのでその備忘録代わりに記事を書いてみました。
+
+## 特殊変数とは
+
+特殊変数とは
+シェルによって自動的に値が設定される特殊な変数がいくつかあり、それら特殊な変数を参照することにより、様々な情報を取得することができる。
+
+| 変数名 | 説明 |
+|---|---|
+| $$ | 実行シェルのプロセス番号(自分のPID)を格納する変数。 |
+| $? | 最後に実行されたコマンドの終了ステータスを格納する変数。(コマンド成功時には 0 、失敗時には 1 、コマンドやエラーの種類によってはその他の数値もあり)|
+| $! | 最後に呼び出されたバックグラウンドプロセスのプロセス番号を格納する変数。 |
+| $- | シェルに与えられたフラグを格納する変数。 |
+| $1 | シェルスクリプト実行時に指定された引数を格納する変数。 |
+| $# | シェルスクリプト実行時に指定された引数、もしくはsetコマンド実行時に指定された全パラメータが設定される変数を格納する変数。 |
+| $0 | シェルスクリプト自身のファイル名フルパス |
+| $n | シェルスクリプトに与えられた引数を格納する変数。nは1,2,3…と指定できる。 |
+| $@ | シェルスクリプト実行時に指定された全引数が設定される変数を格納する変数。<br /> デリミタを格納する環境変数 IFS にセットされた値で区切られて表示される。 |
+| $* | シェルスクリプト実行時に指定された全引数が設定される変数を格納する変数。<br /> 出力に関してはデリミタを格納する環境変数 IFS の影響を受けない。 |
+| ${@:X:Y} | 複数のパラメータのうち、X番目以降のY個のデータを取得するための変数を格納する変数。 |
+| !$ | 最後に実行されたコマンドに指定していた引数を格納する変数。 |
+| $PIPESTATUS | パイプで連結した各コマンドの終了ステータスが設定される変数。 |
+| $LINENO | この変数を使用している行の行番号が設定される変数。 |
+
+## 基本機能
+
+bashの基本機能で使いやすい機能をご紹介
+
+#### 文法チェックをしたい : noexec
+
+「-n」をつけて実行することでスクリプト内のコマンドは実行されずに文法のみをチェックしてくれる。
+
+``` bash
+# 文法エラーありパターン
+$ bash -n test.sh
+test.sh: 行 5: 予期しないトークン `done' 周辺に構文エラーがあります
+test.sh: 行 5: `done'
+
+# 文法エラーなしパターン
+$ bash -n test.sh
+$
+```
+
+#### 実行内容のトレース : xtrace
+
+「-x」をつけることでスクリプトの実行内容を出力できる
+どのコマンドまで処理が行われたかどういった処理でエラーとなったのかを検出するときに使えます。
+
+``` bash
+bash -x test.sh
++ LOOP_COUNT=3
++ for i in `seq 0 ${LOOP_COUNT}`
++ ls -la
++ echo
+
++ for i in `seq 0 ${LOOP_COUNT}`
++ ls -la
++ echo
+
++ for i in `seq 0 ${LOOP_COUNT}`
++ ls -la
++ echo
+
++ for i in `seq 0 ${LOOP_COUNT}`
++ ls -la
++ echo
+```
+
+#### 未定義変数を検出 : nounset
+
+「-u」を指定することで未定義の変数を検出してくれます。未定義変数を許可したい場合には使えないので注意が必要。
+都度ifで判定とかが回避策になるのでしょうか?
+
+``` bash
+$ bash -u test.sh
+test.sh: 行 5: LOOP_COUNT: 未割り当ての変数で
+```
+
+#### 「*」等によるパス名展開の無効化 : noglob
+
+「-f」を指定するとパス名展開を無効化できる。
+
+``` bash
+$ bash -u test.sh
+ls: '/*' にアクセスできません: そのようなファイルやディレクトリはありません
+```
+
+#### 終了ステータスが0以外のものが検出した時点でスクリプトを終了
+
+「-e」でコマンドの終了ステータスが「0」以外のときに後続のスクリプトを実行せずに終了します。
+
+``` bash
+bash -e test.sh
+ls: 'aa' にアクセスできません: そのようなファイルやディレクトリはありませ
+```
+
+#### trapコマンドでシグナルをハンドル
+
+シグナルハンドラも設定できます。
+下記を例にします。
+
+なんかしらの処理中にその処理を停止したい時があるとします。
+その際に既に生成されているtmpファイルがCtrl + Cで止めるだけだと残っていまい自分で消す必要があります。
+trapコマンドを使用すればCtrl + Cで止めたあとの処理を記述することでその処理が行われます。
+
+``` bash
+$ cat test.sh
+#/bin/bash
+
+trap 'rm -f *.tmp' 1 2
+
+touch test1.tmp
+touch test2.tmp
+
+# なんかしらの処理
+sleep 30
+
+# その後にtmpを削除
+rm -f *.tmp
+```
+
+ちなみにtrapコマンドで指定できるシグナル一覧は「trap -l」で確認できます。
+
+``` bash
+$ trap -l
+ 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
+ 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
+11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
+16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
+21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
+26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
+31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
+38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
+43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
+48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
+53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
+58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
+63) SIGRTMAX-1 64) SIGRTMAX
+
+# ちなみにtrapコマンドの書式は下記
+trap 'コマンド' [シグナル番号|シグナル名]
+```
+
+#### パイプで渡したコマンドの終了ステータスを見る
+
+変数PIPESTATUSを確認すれば各コマンドの終了ステータスが確認できます。
+以下はgrepコマンドが検索に失敗しているので終了ステータスが1となっています。
+
+``` bash
+$ ls | grep "a" | wc ; echo ${PIPESTATUS[@]}
+ 0 0 0
+0 1 0 # 終了ステータスを表示
+
+```
+
+#### デフォルトのシェルを変えたい
+
+使用できるシェルの種類を調べるには、/etc/shells を参照するか、もしくは、chsh -l コマンドを実行します。
+シェルを変更するには、chsh コマンドを使用します。
+
+``` bash
+$ cat /etc/shells
+# /etc/shells: valid login shells
+/bin/sh
+/bin/bash
+/bin/rbash
+/bin/dash
+```
+
+## 組み込みコマンド
+
+組み込みコマンドは「help」と打てば見れます
+その中でもよく使うものだけピックアップ
+
+``` bash
+$ help
+GNU bash, バージョン 4.4.19(1)-release (x86_64-pc-linux-gnu)
+これらのシェルコマンドは内部で定義されています。`help' と入力して一覧を参照してください。
+`help 名前' と入力すると `名前' という関数のより詳しい説明が得られます。
+'info bash' を使用するとシェル全般のより詳しい説明が得られます。
+`man -k' または info を使用すると一覧にないコマンドのより詳しい説明が得られます。
+
+名前の後にアスタリスク (*) がある場合はそのコマンドが無効になっていることを意味します。
+
+ job_spec [&] history [-c] [-d offset] [n] または history -anrw [filename]>
+ (( expression )) if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]>
+ . filename [arguments] jobs [-lnprs] [jobspec ...] または jobs -x command [args]
+ : kill [-s sigspec | -n
+(省略)
+```
+
+#### 子プロセスの終了を待つ:wait
+
+シェルから起動しているプログラムが終了するのを待つ。PIDを指定すればそのプロセスが終了するのを待ち、引数を指定しなかった場合はバックグラウンドで起動したすべてのプロセスの終了を待ち、終了ステータスとして0を返す。
+
+``` bash
+#!/bin/bash
+command1 &
+pid1=$! # command1のプロセスIDを変数pid1に格納
+command2 &
+pid2=$! # command2のプロセスIDを変数pid2に格納
+command3 &
+wait $pid1 $pid2 # command1、command2の終了を待つ
+command4
+```
+
+#### 代入不可の変数:readonly
+
+変数の属性を読み込み専用に変更する
+
+#### 変数のスコープを指定:local
+
+シェル関数内において、そのシェル関数とその子関数内でのみ有効な変数を生成する。
+変数のスコープ(有効範囲)が関数内であることを除けば、コマンドの意味や書式はdeclareと同じ。
+
+#### オプション解析をしたい:getopts
+
+``` bash
+while getopts a:h OPT
+do
+ case $OPT in
+ a) SOMEVAL=$OPTARG
+ ;;
+ h) help
+ ;;
+ *) help
+ ;;
+ esac
+done
+
+shift $(( $OPTIND - 1 ))
+TARGET=$1
+```
+
+#### ファイルの種別判定
+
+``` bash
+$ alias aliasname='echo foo'
+$ type aliasname
+aliasname は `echo foo' のエイリアスです
+$ function functionname { echo foo; }
+$ type functionname
+```
+
+#### ヒアドキュメントで行頭のタブを無視
+
+ヒアドキュメントを書くときにインデントでタブを入れるが表示には不要なときに使える
+「<<」ではなく「<<-」にするだけで使える。
+スペースは取ってくれないので注意
+
+``` bash
+$ cat test.sh
+#!/bin/bash
+
+usage()
+{
+ cat <<- _EOF
+ aaa
+ bbb
+ c # これはスペースなのでだめ
+ _EOF
+
+ exit 1
+}
+
+usage
+
+$ bash test.sh
+aaa
+bbb
+ c
+```
+
+#### 余談:組み込みコマンドって何?
+
+組み込みコマンドとはbash自体に実装されているコマンドであり、bashの振る舞いを変更したり、制御構文として機能したりするコマンドが用意されている。
+組み込みコマンドと外部コマンドどちらにも存在するコマンドとして有名なのがechoです。基本的にシェルスクリプト内でechoを実行すると組み込みコマンドが呼ばれる仕組みとなっています。これは高速化が目的です。
+以下のうように外部コマンドと組み込みコマンドの差を見てみました。結果は一目瞭然。組み込みコマンドの方が高速ですね。
+
+``` bash
+# 組み込みコマンド
+$ time (for ((i=0;i<10000;i++)) ; do echo "aaa bbb ccc" >/dev/null; done)
+real 0m0.095s
+user 0m0.054s
+sys 0m0.039s
+
+# 外部コマンド
+$ time (for ((i=0;i<10000;i++)) ; do /bin/echo "aaa bbb ccc" >/dev/null; done)
+real 0m13.287s
+user 0m7.575s
+sys 0m2.242s
+```
+
+## まとめ
+
+意外と忘れがちなことを列挙してみた。
+便利な機能はまだまだたくさんあると思うのでぜひとも教えてください
+
+## 参考リンク
+
+[検索ではあんまり出ないbashの便利技](https://qiita.com/rsooo/items/ef1d036bcc7282a66d7d)
+[シェルスクリプト (Bash) では組み込みコマンド set を活用しましょう](https://laboradian.com/use-set-in-bash-shell-scripts/)
+[bash - 組み込みコマンド](http://d.hatena.ne.jp/anmino/20090805/1249502378)