シェル芸スパルタン演習(第10回シェル芸勉強会編)
ここ最近、Unixシェルに触る機会が減っており、CLIを使う能力の低下を感じていた。
また、近日中に、第25回シェル芸勉強会の開催が予定されており、参加するにあたり予習をしたいとも考えていた。
そこで、過去に開催されたシェル芸勉強会の問題を活用し、シェル芸力をつける演習をおこなうことにした。
今回は、問題として、第10回シェル芸勉強会の設問を用いた。
実行環境
Arch Linux 4.8.4-1-ARCH
GNU bash 4.3.46
GNU coreutils 8.25-2
GNU diffutils 3.5-1
GNU findutils 4.6.0-2
util-linux 2.28.2-1
grep (GNU grep) 2.26
GNU bc 1.06.95
sed (GNU sed) 4.2.2
gawk 4.1.4
usp Personal Tukubai 20160402 ( Open usp Tukubaiで代用可能)
シェル芸演習
問題文は、以下のURLから。
https://blog.ueda.asia/?p=2627
模範解答は、こちらから。
http://blog.ueda.asia/?p=2378
問1
ファイルの中身の数値を足し合わせる問題。
式を作って、bcにぶち込む。
01 #!/bin/sh
02
03 echo 2 5 9 8 1 3 7 4 |
04 tr ' ' '+' |
05 bc
Tukubaiのplusコマンドで。
01 #!/bin/sh
02
03 plus $(echo 2 5 9 8 1 3 7 4)
以下、コピペ実行用のワンライナーコード。
echo 2 5 9 8 1 3 7 4 | tr ' ' '+' | bc
plus $(echo 2 5 9 8 1 3 7 4)
問2
中身にゴミスペースが混じっているファイルのうち、数字を足し合わせる問題。
ゴミを除去した後、式を作ってbcへ入れる。
01 #!/bin/sh
02
03 cat nums |
04 xargs |
05 tr ' ' '+' |
06 bc
Tukubaiのplusで。
01 #!/bin/sh
02
03 cat nums |
04 xargs plus
以下、コピペ実行用のワンライナーコード。
cat nums | xargs | tr ' ' '+' | bc
cat nums | xargs plus
問3
文字数を数える問題。
1文字1レコードにした後、行数を数えるだけ。
(ただし、不要な改行は除去しておく)
01 #!/bin/sh
02
03 cat genkou |
04 tr -d '\n' |
05 grep -o . |
06 wc -l
以下、コピペ実行用のワンライナーコード。
cat genkou | tr -d '\n' | grep -o . | wc -l
問4
文字列の中から、同じ文字が3つ連続する部分を抽出する問題。
grepで後方参照を使えばOK。
01 #!/bin/sh
02
03 cat hoge |
04 grep -o '\(.\)\1\1' |
05 awk -vFS= '$0=$1'
以下、コピペ実行用のワンライナーコード。
cat hoge | grep -o '\(.\)\1\1' | awk -vFS= '$0=$1'
問5
ディレクトリツリーの中から、ファイルをカレントに移動してくる問題。
findでファイルのみを列挙した後、xargs mvするだけ。
01 #!/bin/sh
02
03 find ./a -type f |
04 xargs -I@ mv @ ./
別解。xargsの代わりに、mvコマンド列を生成するだけ。
01 #!/bin/sh
02
03 find ./a -type f |
04 sed 's;.*;mv & ./;' |
05 sh
以下、コピペ実行用のワンライナーコード。
find ./a -type f | xargs -I@ mv @ ./
find ./a -type f | sed 's;.*;mv & ./;' | sh
問6
ファイルの中身を見て、ディレクトリに分類する問題。
まず、grepでファイル名を中身と共に出力する。
その後、awkでmvコマンド列を生成する。
01 #!/bin/sh
02
03 grep '.*' ./file? |
04 awk -F: '/hoge/{print "mv",$1,"./a"} !/hoge/{print "mv",$1,"./b"}' |
05 sh
以下、コピペ実行用のワンライナーコード。
grep '.*' ./file? | awk -F: '/hoge/{print "mv",$1,"./a"} !/hoge/{print "mv",$1,"./b"}' | sh
問7
2つのファイルの組み合わせを、組み合わせの重複無しで列挙する問題。
awkの連想配列を活用し、重複しているかどうかを判別する。
01 #!/bin/sh
02
03 ls file? |
04 awk '{f[NR]=$1} END{for(i=1;i<=NR;i++){for(j=1;j<=i;j++){print f[j],f[i]}}}' |
05 awk '$1!=$2'
Tukubaiのloopxを使った別解。
上の解答とあまり変わらないかも。
01 #!/bin/bash
02
03 loopx <(ls file?) <(ls file?) |
04 awk '{printf("%s", comb[$2,$1]==0 ? $0"\n" : ""); comb[$1,$2]++}' |
05 awk '$1!=$2'
以下、コピペ実行用のワンライナーコード。
ls file? | awk '{f[NR]=$1} END{for(i=1;i<=NR;i++){for(j=1;j<=i;j++){print f[j],f[i]}}}' | awk '$1!=$2'
loopx <(ls file?) <(ls file?) | awk '{printf("%s", comb[$2,$1]==0 ? $0"\n" : ""); comb[$1,$2]++}' | awk '$1!=$2'
問8
特定の範囲にある数値をランダムに出力する問題。
whileるなり、yesるなり、何なり。
1 #!/bin/sh
2
3 while :; do
4 (seq 0 999999 |shuf |head -n 1)
5 done
Tukubai版。
入力から乱数を発生させるコマンド、そのまんまな名前のransuコマンドを使った。
01 #!/bin/sh
02
03 yes 'seq 0 999999 |ransu 1' |
04 sh
以下、コピペ実行用のワンライナーコード。
while :; do (seq 0 999999 |shuf |head -n 1); done
yes 'seq 0 999999 |ransu 1' | sh
雑記
オオカミ可愛い🐺(書くネタが無い)
ゲンキです。ご機嫌です。...おでこに葉っぱがくっついています(笑) 2016.10.01(土) #東山動物園 #シンリンオオカミ ゲンキ pic.twitter.com/GvYJIs9NCe
— ginjiro (@gin_135) 2016年10月7日