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

使う技術

問題

Q1 中身0byteのファイルaを作成してください
a1.sh
touch a : > a echo -n "" > a
問題の意図
ダミーファイルが欲しくなるときはそれなりにあるので
空ファイル作る方法はいくつかあるけれど echo > a ではだめ
改行文字も含まれてしまう
解説
touch もとはファイルのタイムスタンプを更新するコマンドだけど空ファイルを作るのにも使える
: は何もしないコマンドで、その出力をファイルに書き出している
echo -n は改行なしで出力する

Q2 以下のテキストファイルaの中の文字列をすべて小文字に変換して上書きしてください
q2.sh
cat << EOS | tee a HELLO WORLD SUSHI FooBar EOS

a2.sh
cat a | tr '[:upper:]' '[:lower:]' > b mv b a

問題の意図
小文字大文字変換の方法を知る
sed だとめんどくさい
解説
tr は処理するデータを標準入力からしか受け付けないので一旦 cat した結果をパイプで渡す
tr には置換のパターンがいくつか用意されているのでそれを使う
この例だと大文字を小文字に変換する
いきなりリダイレクトでファイルaを上書きするとファイルの中身が消える
一旦別ファイルに吐き出してから mv で移動するところまでやって正解
spongeコマンドを使っても良い

Q3 1 ÷ 3 と -1 ÷ 3 を計算して小数点以下6桁まで出力してください
期待値は以下の通りです
q3.expect
0.333333 -0.333333

a3.sh
# bc の例 echo 'scale=6; 1/3' | bc | sed -E 's/^(-)?\./\10./' echo 'scale=6; -1/3' | bc | sed -E 's/^(-)?\./\10./' # awkの例 awk 'BEGIN{ print 1/3; print -1/3 }'

問題の意図
bashの算術演算式だと小数点以下の計算や出力ができないのでやり方を把握する
解説
bc を使う場合は scale で小数点以下の桁数を指定する
整数部0が消えるのがトラップ
0を付け足す正規表現を書く
^(-)?\.
^ 行頭
(-)? - が1つあってもなくても良い
\. . 文字 ( . は正規表現で使用する特殊な文字なのでエスケープが必要
\10.
\1 前述の正規表現でマッチした部分をグルーピングした結果を参照する
0. 普通の文字としての 0.
awk なら普通にそのまま計算すればよい

Q4 以下の複数のディレクトリ配下でそれぞれmakeしてください
a4.sh
mkdir prog_{1,2,3} echo $'build:\n\techo build 1' > prog_1/Makefile echo $'build:\n\techo build 2' > prog_2/Makefile echo $'build:\n\techo build 3' > prog_3/Makefile find prog_*

a4.sh
for d in prog_*; do (cd $d && make) done

問題の意図
スクリプトとかで、一旦ディレクトリを移動して戻る処理をどう書くかを考える
こういうふうに書くと失敗する
sh
for d in prog_*; do cd $d make done
一旦ディレクトリを移動した先から更に移動しようとするのでダメ
ありがちなミス
解説
() でくくっている部分はサブシェル
サブシェル内部は別のシェルプロセス
別シェルプロセス内でchange directoryしても親プロセスには影響しない
ループ内だけサブシェルにすることで、次のループに影響を残さない

Q5 1コマンドで1 3 5 7 9 ... 20を出力してください
a5.sh
awk 'BEGIN{ for(i=0; i<20; i+=2) { print i+1 }; print 20 }' seq 1 2 20 # NG echo {1..20..2} 20 # egplさんの解答

問題の意図
seq の使い方 (フェイク)
解説
seq は等差数列を生成する際の交差の値を指定できる
この場合は初項1から20までを交差2で生成する
が、最後の数値20は出力されないのでNG
結局 awk で解く
egpl さんの解答のほうがスマートで良い

Q6 以下のテキストファイルaの中身を文字化けしないように画面に出力してください
q6.sh
echo 'jvWOaQo=' | base64 -d > a

a6.sh
# 僕の用意した答え nkf --guess a cat a | iconv -f SJIS -t UTF-8 # タイムラインに流れてきた答え nkf -w a

問題の意図
文字コードを変換する処理
解説
iconv
元はSJISの「寿司」をBase64エンコードしたもの
問題を出題するときにパット見何が書かれてるかわからなくしたくてBase64にした
nkf --guess で文字コードを調べられる
SJISだとわかったので iconv でSJISをUTF8に変換する
nkf -w
タイムラインに流れてきて知ったけれど -w でも良い

Q7 /etc/passwdの各列の桁数をそろえて人間が読みやすい形で出力してください。不足の列がずれないように配慮してください
a7.sh
cat /etc/passwd | sed "s/::/: :/g" | column -s : -t | textimg -s #シェル芸

問題の意図
column コマンド使うと見やすくなるよ
解説
column -t でいい感じに桁を揃えて出力できる
column -s : で区切り文字を指定できる
ただし、これだけだと /etc/passwd には空の列も存在するので、一部の列がずれる
sed の部分で空白文字を挟むことで解決

Q8 カレントディレクトリの末尾2階層分の文字列をとりだして結合して出力してください
q8.sh
echo "例: /work/proj1/app から proj1app という文字列を取り出す" mkdir -p /work/proj1/app cd $_

a8.sh
pwd | tr / \\n | tail -n 2 | tr -d \\n

問題の意図
この前友人に相談されたことで、それをそのまま問題にしてみた
解き方色々ありそうでどうなるかなぁと
解説
pwd でカレントディレクトリを出力
tr でディレクトリの区切り文字を改行文字に置換
改行されたディレクトリ名の末尾2行を取得
改行文字を削除することで結合

以上

余談
trを使った簡単な問題が思いついたので満足
ゆるふわシェル問題 その1ゆるふわシェル問題 その2に比べると難しくなってきたかも
と思ったけれどその1もその2もコマンド知ってないと難しそうなのあるな
個人的にはQ7が一番難しいかな、と