generated at
ゆるふわシェル問題 その1
2020/2/10にシェル芸botに突発的に投下したシェル問題集
全部で8問
そんなに難しくないはず
だいたい1コマンドで解ける

使う技術

問題

Q1. 以下のテキストファイルaに含まれる犬郎を太郎に直せ
a
山田犬郎 犬神健一 藤崎宗犬郎
問題の意図
sed を使う
犬を太に変換するだけだと犬神さんも書き換わっちゃう点に注意する
tr だとマルチバイト文字の置換がうまくいかない
Linux tr では1byteずつ置換する
答え
q1.sh
sed "s/犬郎/太郎/g" a

Q2 カレンダーの曜日、毎日休みがいいので月から金まで日曜日にしてください。
cal
2月 2020 日 月 火 水 木 金 土 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
問題の意図
複数の文字を sed で置換する
sed y コマンドか正規表現を使うのが簡単かなぁと思ってる
答え
q2_1.sh
cal | sed '2y/月火水木金/日日日日日/'
解説
y は置換対象と置換後を1文字ずつ対応して置換する
上のだと月は1文字目の日と対応して置換される
q2_2.sh
cal | sed -E '2s/[月火水木金]/日/g'
解説
-E で拡張正規表現を有効にする
[月火水木金] は月火水木金のいずれかの文字にマッチする
でいずれかにマッチしたものを日に置換する
末尾の g は行内でマッチするすべてを置換する
これがないと月だけ置換して終わる
追記
2s///g みたいに先頭に 2 って付けないと1行目の 2月 まで置換されてしまっていた

Q3 以下のテキストファイルaから山田太郎さんを抽出してください。表記のムラも考慮してください
a
山崎太郎 山田太郎 ヤマダタロウ 山田たろう 山田 太郎 山田 太郎 山本太郎 山田一郎
問題の意図
単純に grep するだけだとどれかを取りこぼすか余計なものが交じる
どうやって複数のテキストを抽出するか
正規表現を使うのを想定してた
答え
q3.sh
grep -E "(山田|ヤマダ)( | )?(太郎|タロウ|たろう)" a
解説
-E で拡張正規表現を有効にする
(山田|ヤマダ) では山田かヤマダにマッチする
( | ) では半角スペースか全角スペースにマッチする
? では直前の文字が1つ存在する場合としない場合にマッチする
(太郎|タロウ|たろう) で太郎かタロウかたろうにマッチする

Q4 以下のCSVファイル(大嘘)の2列めを取り出してください
a
ID、名前、趣味 1、田中、野球 2、山本、サッカー 3、鈴木、バレー
問題の意図
cut ではマルチバイト文字を区切り文字に指定できない
awk を使う必要がある
答え
q4.sh
awk -F 、 '{print $2}' a
解説
-F、 で区切り文字に、を指定
$2 で2列目の文字を指定

Q5 以下の点数表の平均値を求めてください
a
name,score 田中,89 山田,60 鈴木,75 村田,95
問題の意図
awk で簡単な集計をする
平均値なので合計値を全体数で割る時に工夫が必要
awk NR 変数を割る時の全体数に使うとヘッダの行数も含まれてるので1引く必要がある
答え
q5.sh
awk -F , '{ total += $2 } END { print total / (NR-1) }' a
79.75
この辺の解説は awk でググればいっぱいヒットするのでそっちで見て(なげっぱなし)

Q6 echoを使わずに寿司と標準出力してください
問題の意図
なんとなく
他の人はどういうアプローチするかなぁというのが気になっただけ
答え
ヒアストリングを使うのがパッと思いついた
q6.sh
cat <<< 寿司

Q7 以下のような階段状の文字列を出力してください
ヒアストリングはNGで!
これヒアドキュメントの間違いだった
a
1 11 111 1111 11111 111111
問題の意図
繰り返し処理をどうやるか
awk を使うのが一般的かなぁと思ってたけれど for でもできると思う
答え
q7.sh
seq 6 | awk '{ s = ""; for (i=0; i<NR; i++) { s = s "1" } print s }'

Q8. /work ディレクトリの下に2019/01 〜 2020/12までのディレクトリを作成してください。月はサブディレクトリを切ってください
問題の意図
実務ですぐ使えるシェルが役立つシーンの一つだと思ってだした
ループ処理をどうやるか
mkdir のオプションを知っているか
ブレース展開を知っているか
答え
q8.sh
mkdir -p /work/20{19,20}/{01..12}
解説
-p で存在しない親ディレクトリごと一気に作成できる
{} でブレース展開
{19,20} は19と20の文字列を半角スペース区切りで作る
{01..12} は01から12の文字列を半角スペース区切りで作る
これらのブレース展開はブレース展開と隣接する文字を合わせて展開する
sh
echo a_{01..12} # a_01 a_02 a_03 a_04 a_05 a_06 a_07 a_08 a_09 a_10 a_11 a_12
これをそのまま touch すれば空ファイルが作れる
mkdir すればディレクトリが作れる

以上

余談
問題考えるの難しい!!!
cat, grep, sed, awk, 正規表現とメジャーどころは抑えた気がする
sort, uniqもよく使うけれど簡単な問題が思いつかなかった
forやwhile使うやつもあってよかったかなぁ
cut, join, trとかはチョット考えたけどお見送り
joinはオプション自体が複雑だしなぁ〜〜〜
実務オペレーションでよく使うコマンドとかで問題だしてみたいなぁ
nginxのアクセスログの調査とか
件数集計とソートとか
圧縮ファイルの操作とか
ファイルの探し方とか
trapとか
バックグラウンド処理と
日付処理とか
ファイル作成とか
ダミーファイル作成とか
なんかLPICの問題みたいだな・・・