僕がよく使うShellコマンドまとめ

個人的によく使ったShellコマンドを備忘録としてまとめておきます.

zshで動かしているため,bashだと若干動かなかったりする🙇‍♂️

合計を求める

合計を求める時は,awkを利用しています. awkコマンドは,最高に良くできるコマンドなので多用しまくりです.

## 指定したカラムの合計
❯❯❯ echo "1\n2\n3" | awk '{a += $1} END {print a}'
6

## 列ごとの合計
❯❯❯ echo "1 2 3" | awk '{for (i = 1; i <= NF; i++) a += $i} END {print a}'
6

awkコマンドについて少し解説すると,awkは渡ってきた文字列を空白区切りで分割しawk内部で$1,$2...として扱えるようにしてくれます. だからカラムの合計を求める時には,aという変数にひたすら$1を足してますね.

そしてもう一つNFというものがあると思います. これは空白区切りで分割した配列的なやつの長さだと思ってください. awk$0が分割前の元の文字列を意味するので,$1から初めて$NFまでforで回してあげています. そしてENDという文字の後に書いた処理は,最後に一回だけ呼ばれます. なので,出力は一回しかされないようにできます. 結構がっつり解説しましたがニュアンスが伝わればいいと思います.

平均を求める

## 指定したカラムの平均
❯❯❯ echo "1\n2\n3" | awk '{a += $1} END {print a/NR}'
2

## 列ごとの平均
❯❯❯ echo "1 2 3" | awk '{for (i = 1; i <= NF; i++) a += $i} END {print a/NF}'
2

NRは行数と思ってもらうといいと思います.

頻度を求める

ここから少しトリッキーな感じになっていきます.

❯❯❯ echo "1\n2\n3\n1" | awk '{a[$1]+=1} END {for (i in a) print i "," a[i]}' | sort
1,2
2,1
3,1

まずa[$1]a連想配列のように扱えます. そしてfor (i in a)とすることで,keyを取り出せます. 後は出力しておしまいです. 連想配列の指定したkeyに値が存在しなければ,その中身が0なり空文字なりに色々なってくれてすごいけど,バグ起きたらやばそうという印象です.

## 文字列連結もできる
❯❯❯ echo "1\n2\n3\n1" | awk '{a[$1]=a[$1] $1} END {for (i in a) print i "," a[i]}' | sort
1,11
2,2
3,3

## 文字列の例
❯❯❯ echo "hello\nworld\nsunday\nhello" | awk '{a[$1]=a[$1] $1} END {for (i in a) print i "," a[i]}' | sort
hello,hellohello
sunday,sunday
world,world

zip関数みたいなもの

pasteコマンドを利用します.

❯❯❯ ARR1=`seq 1 10`
❯❯❯ ARR2=`seq 21 30`
❯❯❯ paste <(echo $ARR1) <(echo $ARR2) | while read a b ; do echo $a $b ;done
1 21
2 22
3 23
4 24
5 25
6 26
7 27
8 28
9 29
10 30

## 変数に置くのがめんどくさければ以下のようにも書ける
❯❯❯ paste <(for i in `seq 1 10`;do echo $i;done) <(for i in `seq 21 30`;do echo $i;done) | while read a b ; do echo $a $b ;done
1 21
2 22
3 23
4 24
5 25
6 26
7 27
8 28
9 29
10 30

これによって,二つの異なるパラメータが入ったarrayを回してプログラムの引数にしたりなんかもできたりします. そしてここでちゃっかりプロセス置換というものを利用しています. このプロセス置換というものは,コマンドの書き込み先をそのままコマンドに渡せるという感じのものです. これの何が嬉しいのかというと,ファイルを作成しなくてもファイルを参照するようなコマンド(diffなど)に値を渡せます. diff <(hoge) <(hoge)などはよく使うので,覚えておくと良さげだと思います.

# echoで確認するとこんな感じになります
❯❯❯ echo <(echo hello)
/dev/fd/12

指定ディレクトリ内のファイルを回す

❯❯❯ ls dir
a.csv b.csv c.csv

# for
❯❯❯ for i in dir/*.csv ;do echo $i hello;done
dir/a.csv hello
dir/b.csv hello
dir/c.csv hello

# find while
❯❯❯ find dir -name "*.csv" | while read file ;do echo $file hello ;done
dir/a.csv hello
dir/c.csv hello
dir/b.csv hello

# find xargs
❯❯❯ find dir -name "*.csv" | xargs -I@ echo @ hello
dir/a.csv hello
dir/c.csv hello
dir/b.csv hello

好きなものを使えばいいと思っています. 個人的にはfind書いて,while readしていることが多い.

置換

sedコマンドをよく使います

❯❯❯ echo "hello\nworld\nsunday\nhello" | sed 's/he/hu/g' | sed 's/su/mo/g'
hullo
world
monday
hullo


## ファイルの中身を置換する
❯❯❯ cat test.csv
a
b
c
d
a
❯❯❯ sed -i -e 's/a/g/g' test.csv
❯❯❯ cat test.csv
g
b
c
d
g
❯❯❯ cat test.csv-e
a
b
c
d
a

sed -i <extension>と書くことで,書き換えを行う前のファイルのバックアップが取られます. その時のバックアップファイル名は,fileName + <extension>となります. 設定ファイルを少し書き換えたい時なんかは,結構便利です.

まとめ

とりあえず僕がよく使ってきたコマンドたちを紹介しました. この機会にぜひShellと戯れてくださいね.

おまけ

僕はターミナルで長いコマンドを打ったりするのですが,その時にはzshを利用しています. zshの良い点として,vim modeなるものがあります.(bashにもあるらしい) これはvimのようにノーマルモード,インサートモード,ヴィジュアルモードと切り替えができるだけでなく,アンドゥなどもできます. だからとりあえず少しミスっても安心感がでかく,ずっとShellで長いコマンドを書き続けられています.(それがいいのかは知らない) 皆さんも興味があればぜひ使ってみてください.