1. 変数
変数定義
$ name="banana mikan"
イコールとの間にスペースを入れない.
入れるとそれぞれコマンドとして認識してしまいます.
変数利用
$ echo $name # 変数を利用するときは$を付ける
$ echo "$name" # 変数内文字列にスペースがあっても安心
$ echo ${name} # 変数の範囲明示
特殊変数
状況に応じてシステムが定義する変数
| 変数 | 意味 | echo 出力例 |
|---|---|---|
| $? | 直前のコマンドの終了ステータス | 0 |
| $0 | スクリプト名 | -bash |
| \$1,\$2... | シェルスクリプトに渡された引数 | echo $1 -> banana |
| $@ | 全部の引数 | banana mikan |
| $# | 引数の数 | 2 |
| $$ | 今動いているシェルのプロセスID | (例) 583 |
| $! | 最後にバックグラウンド実行したジョブのPID | (例) 770 |
| !$ | 直前のコマンドの最後の引数 | mikan |
| !! | 直前のコマンドを再実行 | - |
| !(番号) | historyの(番号)番を実行 | - |
管理者権限が必要だったって時に,直前のコマンドにsudoを付けて実行するより,
sudo !!で良いのは目から鱗です.
2. パイプとリダイレクト
リダイレクトは操作に必要なファイルディスクリプタの接続先を変更する機能です.
Linuxでは,プログラムが起動した時点で,次の3つのファイルディスクリプタが自動的に用意されています.
| ファイルディスクリプタ | 入出力先 | 初期状態 |
|---|---|---|
| 0 | 標準入力 | キーボード |
| 1 | 標準出力 | コンソール画面 |
| 2 | 標準エラー出力 | コンソール画面 |
| 3 ~ | 任意の入出力先 | - |
普段は標準出力は画面に表示されます.
$ echo "Hello"
Hello
しかし,
echo "Hello" > output.txt
とすると,標準出力が画面からファイルに変更されます.
| 記号 | 意味 |
|---|---|
| < | 読み込み |
| > | 書き込み |
| >> | 書き込み(追記) |
| <> | 読み書き |
とまぁこれ以上の深い説明は他の記事を見るとして,
「3以上のファイルディスクリプタっていつどう使うのだろう..?」
ってのを見ていきます.
3番以降は特殊なものでもなく,基本的には同じように使えます.
そもそもファイルディスクリプタはあくまで開かれているファイルに番号を付けただけです.
C言語でファイルを開いたときも,返り値としてファイルディスクリプタが返ってくるのが分かります.
open("file.txt", O_RDONLY);
↓
/* fd == 3 */
bashでファイルを開きっぱにしてファイルディスクリプタ番号を付ける時はexecが使えます.
exec 3> output.txt
これで,FD3 = output.txtとなるので,あとはフツーに使えます.
echo "Hello" >&3 # output.txtにHelloを書き込む
終わったら,
exec 3>&-
でファイルを閉じることが出来ます.
じゃあ,これがいつ使えるのかというと,
① 出力をいくつもの場所に出したい
exec 3>&1
exec > log.txt
とすると,1 → log.txt,3 → terminalとなるので,
echo "ログ"
echo "画面" >&3
と柔軟に書き出すことができます.
② flock
flockはファイルに対して排他ロックを取得するコマンドです.
exec 9> /tmp/backup.lock
flock 9
これで/tmp/backup.lockのロックを取得できます.
他のプログラムもflockを利用している場合は,ロックが解放されるまで待機(または失敗)するため,同じ処理を同時に実行しないよう制御できます.
処理が終わったら,ファイルディスクリプタを閉じることを忘れないようにしましょう.
exec 9>&-
③ coproc
coprocは別プロセスと双方向通信を行うための仕組みです.
bashは内部で2本のパイプを作成し,その両端を管理するために空いているファイルディスクリプタを割り当てています.
それを確認してみましょう.
この環境では,FD60とFD63で通信していることが分かります.
$ coproc CAT { cat; }
$ ls -l /proc/$$/fd
...
... 60 -> 'pipe:[4399368]'|
... 63 -> 'pipe:[4399367]'|
3. grep
やっぱgrepは使えるようになっておかないと..
-rのオプションぐらいしか使ってなかったけど,他にもこんぐらいある
とりあえず,-rnのコンボは必須ですね
| オプション | 効果 | 引数 |
|---|---|---|
| -n | 行番号表示 | - |
| -i | 大文字小文字を区別しない | - |
| -v | 除外 | (除外する文字) |
| -E | 正規表現を使う | |
| -A -B -C | 前後を表示(-Aが次,-Bが前,-Cが両方) | 表示したい行数 |
| -l | ファイル名だけ表示 | - |
| -c | 件数表示(各ファイルに対して数を表示する) | - |
| -w | 単語完全一致のみ | - |
| -o | マッチした部分だけ表示 | - |
ちなみに,grepは
g/re/p globalコマンド / re(正規表現) / printコマンド
という,edでの一連のコマンドから来ています.
4. sed
次のawkと合わせてシェルスクリプトでファイルを操作するときにとっても便利なコマンドです.
sedが行で見るのが得意,awkが列で見るのが得意
sedは,ファイルやコマンドの出力に対して文字列の置換や行の削除などを行う際によく利用されます.
例えば,
- 特定の文字列を置換する(sed 's/old/new/g')
- 指定した行を削除する(sed '/pattern/d')
- 特定の行だけを表示する(sed '/pattern/p')
といった処理を1行で実現できます.
sedやawkのコマンドはUnix最最初期にはありませんでした.その頃はgrep,sort,uniq,cutなどの小さな処理を行えるコマンドを組み合わせて,ファイル操作を実現していたそうです.
5. awk
awkはテキストを列(フィールド)単位で処理するためのコマンドです.
特にコマンドの出力結果って成形されているものが多いので,それを抽出するのに役に立ちます.
例えば,
- 2列目だけ表示する
- 合計や平均を計算する
- 条件に一致する行だけ処理する
といったことを簡単に行えます.
参考になるかも
6. xargs
xargsは,標準入力から受け取った内容をコマンドライン引数へ変換するコマンドです.
標準入力を受け付けていない,コマンド引数のみ受け付けるものに対して変換してくれます.
find . -name "*.log" | xargs rm
とすると,このように変換して実行してくれます.
rm file1.log file2.log file3.log
7. sort
sortは文字列や数値を並べ替えるコマンドです.使ったことありませんでした.
grepやawkなどで加工した結果を整理する際によく利用されます.
例えば,
$ find / -type f | awk -F. '{print $NF}' | sort | uniq -c | sort -nr | head
97532 h
85095 py
72798 pyc
50583 html
42132 go
33739 js
32611 svg
29600 png
25540 pl
23835 gz
これで拡張子多いランキングを作ったりできます.
8. uniq
uniqは隣り合った重複を消してくれるコマンドです.
入力を先頭から1行ずつ読み込み,直前の1行だけを覚えながら処理します.
隣り合っていないと,消してくれません.
隣り合っていないものも消す=過去に登場したすべての行を記憶する必要があり,メモリなり資源を多く消費してしまうためです.
apple
banana
mikan
apple
banana
mikan
❌こうしてくれない
apple
banana
mikan
そのため,sortコマンドと組み合わせて使うことが一般的です.
sort sample.txt | uniq > tmp.txt
mv tmp.txt sample.txt
# この処理だけならsortだけでできる
# sort -u sample.txt -o sample.txt
# (重複削除) (出力先指定)
9. tee
teeは画面出力,ファイル書き込みを両方行ってくれるコマンドです.
tee file.txt
けど,使い道が分からない.複数ファイルに出力することもできますが,他のコマンドで代用できるので..
他にも例えば,
echo "Hello" > output.txt
これを管理者権限で実行するとき,単純にsudoをつけただけでは想定する動作をしないでしょう.
$ sudo echo "Hello" > output.txt
-bash: output.txt: 許可がありません
これはechoという "コマンド" の処理へと渡す前にリダイレクトの解決をシェルが行おうとするためです.
$ sudo echo "Hello" > output.txt
シェル(ユーザ権限):sudoの処理だ!じゃあその前に自分のやることないかな?(">" や "<" を探す)
シェル(ユーザ権限):あった! じゃあ,FD1の先をoutput.txtにしとこう
↓
❌書き込み権限不足
この時にteeを橋渡し役として使えます.
echo "Hello" | sudo tee output.txt
まぁ,正直こうでいい
sudo bash -c 'echo "Hello" > output.txt'
10. tr
trは1文字単位で変換・削除・圧縮を行うコマンドです.translateから来ています.
cutコマンドやuniqコマンドとか,割と他にくわれ気味なものがいる中で,trはしぶとく残っています.たぶん
$ echo "Hello" | tr A-Z a-z
hello
特に,
tr -d '\r' # windowsの改行を消す
これは今でもよく見ます.
11. コマンド置換
Bourne shではバッククォートで囲んでいたのが,bashでは$と括弧で囲って使えるようになってました.
i=`expr $i + 1`
↓
i=$(expr $i + 1)
↓ (bashの機能でさらに工夫できる)
i=$((i + 1))
12. test と [ ]
if文では条件判定のtestコマンドを利用することが多いです.
test 条件
echo $? # 0なら真,1なら偽
これと下記の書き方は一緒です.
[ 条件 ] # 括弧の中はスペースが必要
参考になるかも
13. bash配列
bashでは配列を利用できます。
arr=("a" "b" "c")
各要素へは添字でアクセスします。
echo "${arr[0]}" # a
echo "${arr[1]}" # b
echo "${arr[@]}" # 全要素
echo "${#arr[@]}" # 要素数
また,for文と組み合わせることで全要素を簡単に処理できます。
for i in "${arr[@]}"; do
echo "$i"
done
14. シグナル
trapはシグナルを受け取ったときの動作を変更できるコマンドです.
例えば,
スクリプトが終了したときに,一時ファイルを削除して終了する
というとても便利な使い方ができます☆
tmp=$(mktemp)
trap 'rm -f "$tmp"' EXIT