シェル力が圧倒的に足りてないのでシェル力を養うためにシェル芸勉強会の問題に挑戦してみた。
シェル力が圧倒的に足りてないのでシェル力を養うためにシェル芸勉強会の問題に挑戦してみた。
感想から先に書いてしまうとパズル感覚で楽しいしシェル力を養うのにも役立ちそう。
自分が使ってる zsh のバージョン。
$ zsh --version zsh 5.8 (x86_64-apple-darwin19.6.0)
はじめに
自分はシェル力が圧倒的に足りてないので勉強のためにシェル芸勉強会の問題に挑戦してみた。
一応自分がシェルを使う用途を書いておくと普段の仕事は Mac を使ってやっててちょっとしたことをやるのにちょろっとシェルを使う程度。
ちなみにシェル芸ってなに? ってところは Wikipedia の USP 友の会 から引用しておく。
シェル芸とは、主にUNIX系オペレーティングシステムにおいて「マウスも使わず、ソースコードも残さず、GUIツールを立ち上げる間もなく、あらゆる調査・計算・テキスト処理を CLI端末へのコマンド入力一撃で終わらせること」(USP友の会会長・上田隆一による定義[9])である。この技術を持つ人物を指すシェル芸人という呼び方も存在する[10]。
古典的には、UNIX系OSの各種CUIシェル上で単一または複数のコマンドを一行のコマンドラインでをつなぎ合わせて処理をおこなう、いわゆる「ワンライナー」系の手順にあたる。
第10回シェル芸勉強会の問題に挑戦してみた
シェル芸勉強会という勉強会の存在は知ってたけどちゃんと見たことはなかったので、なんとなくキリがよさそうな 第10回シェル芸勉強会 の問題に挑戦してみた。
Q1
Question
次の数字の列を足し算してください。(できる人はなるべく変態的に)
$ echo 2 5 9 8 1 3 7 4 2 5 9 8 1 3 7 4
Answer
数値列を行に変換して awk コマンドで集計した。
$ echo 2 5 9 8 1 3 7 4 | tr ' ' '\n' | awk '{SUM+=$1}END{print SUM}' 39
別解。数値列を行に変換するとこは tr コマンドじゃなくて dango コマンドで変換した。
$ echo 2 5 9 8 1 3 7 4 | dango -w -n 1 | awk '{SUM+=$1}END{print SUM}' 39
別解その2。数値列をそのまま awk コマンドで集計した。
$ echo 2 5 9 8 1 3 7 4 | awk '{for(i=1;i<=NF;i++){SUM+=$i}}END{print SUM}' 39
Q2
Question
スペースと数字と改行を使って次のようなファイルを作り、書いた数を足し算してください。
$ cat nums 1 2 3 4 5 6 7 8 9
Answer
数値列を行に変換して awk コマンドで集計した。
$ cat nums | tr ' ' '\n' | awk '{SUM+=$0} END{print SUM}' 45
別解。数値列を行に変換するとこは tr コマンドじゃなくて dango コマンドで変換した。
$ cat nums | dango -w -n 1 | awk '{SUM+=$0} END{print SUM}' 45
Q3
Question
文字数を数えてください。改行記号は数えないでください。
ueda@remote:~$ cat genkou 筆者は朝、目玉焼きを食べた。 昼、著者は卵がけごはんを食べた。 そして夜、著者はマンハッタンの夜景を 見ながらゆで玉子を食べた。
Answer
改行記号は tr コマンドで削除して wc -m
で文字数を数えた。
$ cat genkou | tr -d '\n' | wc -m 61
Q4
Q3 まではほとんど考えずに解けたけどこのへんからちょっと頭を使う感じになってきた。
Question
次のようなファイルを作り、ファイルの中に三個存在する文字を出力してください。
ueda@remote:~$ cat hoge aabbcdabbcccdd
Answer
hoge ファイルの文字列を行に変換して uniq -c
で数えて awk コマンドで3個存在する文字だけを表示した。
$ cat hoge | sed 's/./&\n/g' | sort | uniq -c | awk '$1==3{print $2}' a d
別解。文字列を行に変換するところは sed コマンドじゃなくて dango コマンドで変換した。
$ cat hoge | dango -c -n 1 | sort | uniq -c | awk '$1==3{print $2}' a d
Q5
Question
次のようなファイル、ディレクトリを作ってください。そして、file1, file2, file3をカレントディレクトリに移動してください。
$ mkdir -p a/b/c $ touch a/file1 a/b/file2 a/b/c/file3 $ tree . └── a ├── b │ ├── c │ │ └── file3 │ └── file2 └── file1 3 directories, 3 files
Answer
find コマンドでファイルだけ探して xargs + mv でファイルをカレントディレクトリに移動した。
$ find ./* -type f | xargs -I{} mv {} ./ $ tree . ├── a │ └── b │ └── c ├── file1 ├── file2 └── file3 3 directories, 3 files
Q6
Question
次のようにファイルとディレクトリを作り、hogeと書いてあるファイルをディレクトリa、 それ以外のファイルをディレクトリbに振り分けてください。
$ echo hoge > file1 $ echo huge > file2 $ echo hoge > file3 $ echo hoge > file4 $ mkdir a b
Answer
while ループでそれぞれのファイルを処理して、ファイルが hoge
を含んでるかは grep コマンドで判定。
$ ls file* | while read f; do grep hoge $f > /dev/null && mv $f a || mv $f b; done $ tree . ├── a │ ├── file1 │ ├── file3 │ └── file4 └── b └── file2 2 directories, 4 files
Q7
Question
以下の9つのファイルについて、二つのファイルの組み合わせを全て列挙してください。ただし、重複してはいけません。
uedambp:~ ueda$ touch file{1..9} uedambp:~ ueda$ ls file{1..9} file1 file2 file3 file4 file5 file6 file7 file8 file9
Answer
2つのファイルの組み合わせを作るのがポイントかなって気がした。
少し考えたけど while ループでそれぞれのファイルとファイル群を echo $f file*
したら2つのファイルの組み合わせを作るもとができるのに気がついたらあとは簡単……と思ったけど重複を弾くのはいろいろ試行錯誤することに。
$ ls | while read f; do echo $f file*; done | awk '{for(i=2;i<NF;i++){print $1,$i}}' | sort | awk '$1<$2'
Q8
シェル芸以前の問題なんだけどシェルで乱数を取得する方法が分からなかったのでググったら半分くらい答えが分かってしまった。
あと cat /dev/urandom
をパイプで tr コマンドに接続したら tr: Illegal byte sequence
のエラーになってどうにもできないみたいだったので、この問題だけ Virtual Box 上の Ubuntu 20.04.2 LTS で解いた。
追記
Twitter で LANG=C tr 〜
したら tr コマンドのエラーが回避できると教えてもらって LANG=C tr 〜
はやっても状況は変わらなかったけど LC_CTYPE=C tr 〜
にしてみたらちゃんと動くようになった。感謝!
Question
0から999999までの数字の一様乱数を無限に出力し続けてください。
Answer
$ cat /dev/urandom | tr -dc '0-9' | fold -b 6 | sed 's/^0*//'