dorapon2000’s diary

忘備録的な。セキュリティとかネットワークすきです。

シェル芸入門忘備録

突然シェル芸が勉強したくなったので、勉強したコマンドたちをまとめました。 シェル芸を忘れた未来の自分と、シェルは使ってるけどシェル芸のコマンドは知らない人向けです。 説明はできるだけ省いて、上から実行結果を見ていくだけで内容とシェル芸の雰囲気がつかめるよう努めました。


実施環境

紹介するコマンド

  • man
  • seq
  • shuf
  • tac
  • rev
  • yes
  • bc
  • tr
  • cut
  • sort
  • uniq
  • sed
  • awk
  • xargs
  • grepオプション
  • lsオプション

man

基本のキ。ホームポジションから手を離さず使い方を思い出せる。

$ man man

seq

連番を生成。1からなので注意。

$ seq 10
1
2
3
4
5
6
7
8
9
10

0から生成。

$ seq 0 5
0
1
2
3
4
5

bashの機能でも似たようなことができる。

$ echo {1..10}
1 2 3 4 5 6 7 8 9 10

shuf

出力の順番をシャッフルする。

$ seq 5 | shuf
3
4
2
1
5

tac

逆順に表示。

$ seq 5 | tac
5
4
3
2
1

rev

各行の文字列をリバースする。

$ echo "いろはにほへと" | rev
とへほにはろい

yes

yを無限出力。

$ yes | head -5
y
y
y
y
y

文字を指定。

$ yes hello! | head  -5
hello!
hello!
hello!
hello!
hello!

bc

文字列を計算する。

$ echo "1+2*3" | bc
7

tr

置換。引数に空白""指定不可。

$ echo 1 2 11 10 2 | tr " " "\n"
1
2
11
10
2

$  echo 1 2 11 10 2 | tr "12" "ab"
a b aa a0 b

特定の文字列削除。

$ echo 1 2 11 10 2 | tr -d " "
1211102

cut

半角空白" "を区切り文字として3フィールド目を取得。区切り文字はデフォルトでタブ\t

$ echo 1 2 11 10 2 | cut -d" " -f3
11

-bでバイト目を取得。-cで文字目を取得だが、ubuntu@WSLではマルチバイト文字があるとエラーを吐いた。Macのcutは正しく動いた。

$ echo hello! | cut -b 2
e

$ echo hello! | cut -b 2-
ello!

sort

$ echo 1 2 11 10 2 | tr " " "\n" | sort
1
10
11
2
2

# --numeric-sort
$ echo 1 2 11 10 2 | tr " " "\n" | sort -n
1
2
2
10
11

# --reverse
$ echo 1 2 11 10 2 | tr " " "\n" | sort -nr
11
10
2
2
1

# --uniq
$  echo 1 2 11 10 2 | tr " " "\n" | sort -nu
1
2
10
11

uniq

重複削除

$ echo 1 2 11 10 2 | tr " " "\n" | sort -n | uniq
1
2
10
11

# --duplicated
$ echo 1 2 11 10 2 | tr " " "\n" | sort -n | uniq -d
2

# --count
$ echo 1 2 11 10 2 | tr " " "\n" | sort -n | uniq -c
     1 1
     2 2
     1 10
     1 11

sed

置換。

# ABC -> XYZ
sed 's/ABC/XYZ/g'

# 10行目~20行目で置換
sed '10,20s/ABC/XYZ/g'

# 2つ置換
sed -e 's/AAA/XXX/g' -e 's/BBB/YYY/g'

# AAAを含む行のBBBをCCCへ置換
sed -e '/AAA/s/BBB/CCC/g'

行の削除。

# 1行目削除
sed 1d

# 1~3行目削除
sed 1,3d

# #から始まる行を削除
sed '/^#/d'

文字の削除。

sed -e "s/ABC//g"

awk

ある種のプログラミング言語なので詳細は他のサイトに譲る。基本はawk PATTERN{ACTION}でif (PATTERN) ACTIONと捉えられる。

空白またはタブで区切られた5番目が10000以上のとき。

$ ls -l / | awk '$5 >= 10000 {print}'
-rwxr-xr-x  1 root root 87944 Jan  1  1970 init

$ ls -l / | awk '$5 >= 10000 {print $5}'
87944

ACTIONを省略すると{print}と同義。~は==の正規表現版。==はなぜか動かなかった。

$ ls -l / | awk '$6 ~ /Sep/'
drwxr-xr-x  1 root root   512 Sep 13 22:07 dev
dr-xr-xr-x 11 root root     0 Sep 13 22:07 proc
drwxr-xr-x  1 root root   512 Sep 13 22:07 run
dr-xr-xr-x 12 root root     0 Sep 13 22:07 sys
drwxrwxrwt  1 root root   512 Sep 12 22:00 tmp

PATTERNを省略すると全行に適応

$ ls -l / | awk '{print $1,$2,$3}' | head -5
total 88
drwxr-xr-x 1 root
drwxr-xr-x 1 root
drwxr-xr-x 1 root
drwxr-xr-x 1 root

$ ls -l / | awk '{print $1$2$3}' | head -5
total88
drwxr-xr-x1root
drwxr-xr-x1root
drwxr-xr-x1root
drwxr-xr-x1root

PATTERNには正規表現も可能

$ ls -l / | sed 1d | awk '/^[^d]/{print $9, "is not a directory"}'
init is not a directory

NRはNum of Recordsで現在行が入るawkの変数。NFもある。

ls -l / |  awk '/^[^d]/ && NR>1{print $9, "is not a directory"}'
init is not a directory

--field-separator。区切り文字をデフォルトの"[ \t]+"から":"に変更。-F","でCSVに使える。

$ cat /etc/passwd | awk -F":" '$7 ~ /bash$/{print $1}'
root
dorapon
  • ENDは最後に1回だけ実行されるPATTERN。BEGINもある。
  • PATERN{ACTION}は並べて複数書ける。
  • 仮想配列もある。ただし出力順番は不定
  • ACTION自身もセミコロン;で複数書ける。
  • for,while,case,ifも雰囲気で使える。
$ ls -l / | awk 'NR>1{month[$6] += $5} END{print "The sum of capacity of each month."; for(m in month) print m,month[m],"bytes"}'
The sum of capacity of each month.
Sep 1536 bytes
Jan 87944 bytes
Apr 5120 bytes
Jul 2560 bytes

xargs

空白タブ改行を一緒くたにまとめる。

$ echo "1 23\n4\t5 6  7\n\t89"
1 23
4       5 6  7
        89

$ echo "1 23\n4\t5 6  7\n\t89" | xargs
1 23 4 5 6 7 89

# 2行ごと改行出力
$ echo "1 23\n4\t5 6  7\n\t89" | xargs -L2
1 23 4 5 6 7
89

# 1行ごと
$ echo "1 23\n4\t5 6  7\n\t89" | xargs -L1
1 23
4 5 6 7
89

$ echo "1 23\n4\t5 6  7\n\t89" | xargs -L1 | tr " " "+" | bc
24
22
89

取得した複数のファイル名を1つづつ引数に取りたい時

$ grep -ils info /var/log/*
/var/log/dpkg.log

# xargsがないと出力自体を引数にとってしまう。
$ grep -ils info /var/log/* | tail -3
/var/log/dpkg.log

$ grep -ils info /var/log/* | xargs tail -3
2018-09-06 22:47:28 trigproc man-db:amd64 2.8.3-2 <none>
2018-09-06 22:47:28 status half-configured man-db:amd64 2.8.3-2
2018-09-06 22:47:33 status installed man-db:amd64 2.8.3-2

# 実際に実行するコマンドは-tオプションで見られる。
$ grep -ils info /var/log/* | xargs -t tail -3
tail -3 /var/log/dpkg.log
2018-09-06 22:47:28 trigproc man-db:amd64 2.8.3-2 <none>
2018-09-06 22:47:28 status half-configured man-db:amd64 2.8.3-2
2018-09-06 22:47:33 status installed man-db:amd64 2.8.3-2

便利なオプション

grep

オプション 説明
-r, --recursive 再帰的に探索 grep Error -r /var/log
-i, --ignore-case 大文字小文字区別なし grep Error -ri /var/log
-l, --files-with-matches ファイル名だけ出力 grep Erro -rl /var/log
-e, --regexp 正規表現
-o, --only-matching 一致した文字を出力 grep -e -o "eth[0-9]\+" hoge.log
-v, --invert-match を含まない grep -v 0 dropstats.log
-s, --nomessages エラー出力なし

ls

オプション 説明
-1 1行で表示 ls -1
-t 更新時間順 ls -lt
-S サイズ順 ls -lS
-F ディレクトリの後ろに/ ls -lF

乱数生成

bash環境変数$RANDOMを使う場合。ただし、0~32767。

$ echo $((RANDOM%+101))
47

連続で

$ yes 'echo $((RANDOM%+101))' | head -5 | bash
21
60
50
70
88

/dev/urandomを使う場合。

echo $(($(od -vAn -N1 -tu < /dev/urandom) % 101 ))
99

参考サイト

用語集:ランダム・乱数まとめ: UNIX/Linuxの部屋

AWKのトリッキーな配列&連想配列の仕組み・動作と目からウロコのテクニック (1/2):CodeZine(コードジン)