【困ったら】第60回シェル芸勉強会【man bash】
当日の昼に自宅からリモート参加しようとしたのですが、エアコンが壊れたため家電量販店に行かざるを得なくなり、結局深夜の空き時間ができたときに1日につき1〜2問くらいのスピードで解答することになりました。
- 名称: jus共催 第60回記念帰ってきたシェル芸勉強会
- 開催日: 2022年7月2日(土)
- アナウンスページ:
- リンク集: https://b.ueda.tech/?post=shellgei_60_link
- Youtubeライブ配信録画: https://www.youtube.com/watch?v=Zyk110aaiLc
-
問題で使われているデータファイルの入手方法:
git clone https://github.com/ryuichiueda/ShellGeiData.git
Q1
次のようにすると、変数A
には、a1 a2 a3 a4 b1 b2 b3 b4 c1 c2 c3 c4 d1 d2 d3 d4
が代入されます。
$ A=$(echo {a,b,c,d}{1,2,3,4})
$ echo $A
a1 a2 a3 a4 b1 b2 b3 b4 c1 c2 c3 c4 d1 d2 d3 d4
同様な代入を、echo
を使わないで実現する方法を考えてください。{a,b,c,d}{1,2,3,4}
という部分はワンライナー中に残しましょう。(できる人は複数お願いします。コマンド置換を使わない方法も考えてみましょう。)
A1
# ファイルは`while`でプロセスがforkされても関係なくグローバルに使える
$ file {a,b,c,d}{1,2,3,4} | awk '{print $1}' | sed 's/:$//' | xargs > /tmp/A1; while read L; do A=$L; done < /tmp/A1; echo $A
a1 a2 a3 a4 b1 b2 b3 b4 c1 c2 c3 c4 d1 d2 d3 d4
# `printf`を使った別解
$ printf '%s ' {a,b,c,d}{1,2,3,4} | sed 's/ $/\n/' | xargs > /tmp/A1; while read L; do A=$L; done < /tmp/A1; echo $A
a1 a2 a3 a4 b1 b2 b3 b4 c1 c2 c3 c4 d1 d2 d3 d4
Q2
1秒ごとに時刻を確認し、秒数が3の倍数のときにアホと出力するbashの関数をなるべく短く書いて実行してください。
秒数が変わる時刻ぴったりに出力する必要はありません。他の文字は出してはいけません。外部コマンドは使って構いません。
A2
# `date`の出力フォーマット指定で「-」を「%」の後に続けてフィールドの空白を埋めないようにする
$ f(){ while :; do [ $(echo $(( $(date '+%-S')%3 )) ) -eq 0 ] && echo アホ; sleep 1; done }; f
アホ
アホ
アホ
(3秒ごとに「アホ」を出力)
Q3
/
を打たずに、ls /
を10回繰り返す(ls / / / / / / / / / /
でも可)方法をいくつか考えてください。できる人は、ls
以外の外部コマンドは使わずにやってみたり、なるべく字数を少ない解答を作ってみたりしましょう。
A3
# bashの機能であるバックスラッシュエスケープシーケンスを使う
$ for i in {1..10}; do ls $'\x2F'; done
bin dev initrd.img lost+found opt run sys var
boot etc initrd.img.old media proc sbin tmp vmlinuz
daily_lock home lib mnt root srv usr vmlinuz.old
bin dev initrd.img lost+found opt run sys var
boot etc initrd.img.old media proc sbin tmp vmlinuz
daily_lock home lib mnt root srv usr vmlinuz.old
bin dev initrd.img lost+found opt run sys var
boot etc initrd.img.old media proc sbin tmp vmlinuz
daily_lock home lib mnt root srv usr vmlinuz.old
(...略...)
Q4
次のdanger
というファイルから、ディレクトリのファイルを消してしまうコマンドが書いてある行を抽出してください。
$ cat danger
rm "*" " " " " " "
rm " * " " " " " " "
rm " " " " " " "*"
rm " " " " * " " " "
rm " " " * " " "
A4
$ while read L; do F=$(echo "$L" | sed 's/^rm/echo/' | sh | tr -d ' *'); [ "$F" == "" ] || echo "$L"; done < danger
rm " " " " * " " " "
Q5
ls -l
の出力には2つ以上続く空白が含まれます。ls
以外に外部コマンドを使わずに、空白をひとつに詰めてください。
A5
# bashのパラメータ展開でパターンを置換するときに使う`//+(A)/B`は正規表現で置換する場合の`s/A+/B/g`に相当
$ S=$(ls -l); echo "${S//+( )/ }"
合計 128
drwxrwxr-x 2 root root 4096 6月 16 14:18 bin
drwxr-xr-x 3 root root 4096 6月 16 14:46 boot
-rw-r--r-- 1 root root 0 12月 24 2021 daily_lock
drwxr-xr-x 18 root root 3300 7月 4 15:15 dev
drwxr-xr-x 178 root root 12288 7月 4 15:15 etc
drwxr-xr-x 5 root root 4096 12月 24 2021 home
lrwxrwxrwx 1 root root 33 6月 16 14:19 initrd.img -> boot/initrd.img-5.10.0-15-686-pae
lrwxrwxrwx 1 root root 33 6月 16 14:19 initrd.img.old -> boot/initrd.img-5.10.0-14-686-pae
drwxr-xr-x 19 root root 4096 3月 27 14:32 lib
(...略...)
Q6
Q6小問1
次のように、ふたつの変数に入れた巨大文字を横に並べる関数を作ってください。paste
等、外部コマンドは使って構いません。
Q6小問2
小問1で作った関数を、よりたくさん引数がとれるように改良してください。
A6
A6(小問1・小問2共通)
# 手持ちの環境では`figlet`の代わりに`toilet`を使わないとQ6の出力例のようにならない
$ a=$(toilet YES)
$ b=$(toilet WE)
$ c=$(toilet CAN)
$ f(){ S='paste'; for I in $(seq ${#@}); do S="$S $(echo "<(echo \"\$$I\")")"; done; eval "$S"; }; f "$a" "$b" "$c"
m m mmmmmm mmmm m m mmmmmm mmm mm mm m
"m m" # #" " # # # # m" " ## #"m #
"#" #mmmmm "#mmm " #"# # #mmmmm # # # # #m #
# # "# ## ##" # # #mm# # # #
# #mmmmm "mmm#" # # #mmmmm "mmm" # # # ##
小問1の解答を作っていたら小問2の解答ができましたので、これを小問1・小問2共通の解答とします。
Q7
次のような出力を得てください。前の問題はあまり関係ありません。できた人は、小問2の関数a
を少し修正して再利用してみましょう。
A7
toilet A | sed 's/$/ /;s/[^ ]/A/g' | toilet
出力は次のようになります(画像への変換にtextimg
を使用)。
Q8
このような挙動を示す変数A
を作ってください。
$ echo $A
c
$ echo $A | grep a
c
A8
# A3と同じく、bashの機能であるバックスラッシュエスケープシーケンス(ここではバックスペース)を使う
$ A=a$'\b'c
$ echo $A
c
$ echo $A | grep a
c