毎度お邪魔しているシェル芸勉強会に行ってきたので、その復習内容を残しておく。 今回は、ちょっと前にようやく買えた TexYoda II を持ってって挑戦してみた。

問題及び回答例はこちら。今回は大分難しい+ミスすると環境破壊系なので、破壊しても良い環境で実施しないと危ない内容なので注意。 また、事前に以下のコマンドで使用するファイル類を取得しておく。

git clone https://github.com/ryuichiueda/ShellGeiData.git

Q1.

絵文字の「💩」を使って、treeで出力した際に同じディレクトリ名でネストして作成するという内容。 さすがにQ1なのでそんなに難しくはなかった。

printf 💩/'%.s' {1..10} | xargs mkdir -p && tree 💩
blacknon@BS-PUB-UBUNTU-01:~/test_dir1$ printf 💩/'%.s' {1..10} | xargs mkdir -p && tree 💩
💩
└── 💩
    └── 💩
        └── 💩
            └── 💩
                └── 💩
                    └── 💩
                        └── 💩
                            └── 💩
                                └── 💩

9 directories, 0 files

Q2.

Cowsay(牛のAAが喋るコマンドのやつ)の出力でファイルを作成し、ls実行時にAAが表示されるようにする、という内容。 ポイントとしては、「/」が含まれるファイルは作成が不可能なので、置換してやる必要があるよという点。

cowsay test | tr '/' '@' | awk '{print $0;system("sleep 1")}' | xargs -n1 -d'\n' touch && ls -ltr | sed 's|@|/|g'
blacknon@BS-PUB-UBUNTU-01:~/test_dir1$ cowsay test | tr '/' '@' | awk '{print $0;system("sleep 1")}' | xargs -n1 -d'\n' touch && ls -ltr | sed 's|@|/|g'
合計 0
-rw-rw-r-- 1 blacknon blacknon 0  1月 28 17:26  ______
-rw-rw-r-- 1 blacknon blacknon 0  1月 28 17:26 < test >
-rw-rw-r-- 1 blacknon blacknon 0  1月 28 17:26  ------
-rw-rw-r-- 1 blacknon blacknon 0  1月 28 17:26         \   ^__^
-rw-rw-r-- 1 blacknon blacknon 0  1月 28 17:26          \  (oo)\_______
-rw-rw-r-- 1 blacknon blacknon 0  1月 28 17:26             (__)\       )\/\
-rw-rw-r-- 1 blacknon blacknon 0  1月 28 17:26                 ||----w |
-rw-rw-r-- 1 blacknon blacknon 0  1月 28 17:26                 ||     ||

Q3.

「あいうえお」の五〇音順でファイルを作成するという内容。 まず、ターミナル上で「あいうえお」を出力する必要があるので、そこだけ抜粋。復習の結果、2通りのやり方を見つけた。

# zshでブレース展開を利用して半角カナを出力して、そこから取得する
zsh -c 'echo {ア..ワ} ヲ ン' | nkf --hiragana | tr -d ' ' | sed -r 's/(や|ゆ|わ|を)/& /g' | fold -15

# uconvでローマ字入力をひらがなに変換して、不要な文字を削除したりして取得する
echo {,k,{s,sh},{t,ts,ch},n,{h,f},m,y,r,w}{a,i,u,e,o} n | uconv -x hiragana | grep -Po '(?<=^|\s).' | sort -u | tr -d '\n' | sed -r 's/(や|ゆ)/& /g;s/ゐ/&う/g' | fold -15

あいうえおの文字列さえ出力できたら、後はxargsでtouchしてやればいい。

blacknon@BS-PUB-UBUNTU-01:~/test_dir2$ ls -la
合計 8
drwxrwxr-x  2 blacknon blacknon 4096  1月 28 21:49 .
drwxr-xr-x 33 blacknon blacknon 4096  1月 28 21:49 ..
blacknon@BS-PUB-UBUNTU-01:~/test_dir2$ zsh -c 'echo {ア..ワ} ヲ ン' | nkf --hiragana | tr -d ' ' | sed -r 's/(や|ゆ|わ|を)/& /g' | fold -15 | xargs -n1 touch
blacknon@BS-PUB-UBUNTU-01:~/test_dir2$ ls -la 
合計 8
drwxrwxr-x  2 blacknon blacknon 4096  1月 28 21:49 .
drwxr-xr-x 33 blacknon blacknon 4096  1月 28 21:49 ..
-rw-rw-r--  1 blacknon blacknon    0  1月 28 21:49 あいうえお
-rw-rw-r--  1 blacknon blacknon    0  1月 28 21:49 かきくけこ
-rw-rw-r--  1 blacknon blacknon    0  1月 28 21:49 さしすせそ
-rw-rw-r--  1 blacknon blacknon    0  1月 28 21:49 たちつてと
-rw-rw-r--  1 blacknon blacknon    0  1月 28 21:49 なにぬねの
-rw-rw-r--  1 blacknon blacknon    0  1月 28 21:49 はひふへほ
-rw-rw-r--  1 blacknon blacknon    0  1月 28 21:49 まみむめも
-rw-rw-r--  1 blacknon blacknon    0  1月 28 21:49 や ゆ よ
-rw-rw-r--  1 blacknon blacknon    0  1月 28 21:49 らりるれろ
-rw-rw-r--  1 blacknon blacknon    0  1月 28 21:49 わ を ん
blacknon@BS-PUB-UBUNTU-01:~/test_dir2$ rm -rf ./*
blacknon@BS-PUB-UBUNTU-01:~/test_dir2$ echo {,k,{s,sh},{t,ts,ch},n,{h,f},m,y,r,w}{a,i,u,e,o} n | uconv -x hiragana | grep -Po '(?<=^|\s).' | sort -u | tr -d '\n' | sed -r 's/(や|ゆ)/& /g;s/ゐ/&う/g' | fold -15 | xargs -n1 touch
blacknon@BS-PUB-UBUNTU-01:~/test_dir2$ ls -la
合計 8
drwxrwxr-x  2 blacknon blacknon 4096  1月 28 21:50 .
drwxr-xr-x 33 blacknon blacknon 4096  1月 28 21:49 ..
-rw-rw-r--  1 blacknon blacknon    0  1月 28 21:50 あいうえお
-rw-rw-r--  1 blacknon blacknon    0  1月 28 21:50 かきくけこ
-rw-rw-r--  1 blacknon blacknon    0  1月 28 21:50 さしすせそ
-rw-rw-r--  1 blacknon blacknon    0  1月 28 21:50 たちつてと
-rw-rw-r--  1 blacknon blacknon    0  1月 28 21:50 なにぬねの
-rw-rw-r--  1 blacknon blacknon    0  1月 28 21:50 はひふへほ
-rw-rw-r--  1 blacknon blacknon    0  1月 28 21:50 まみむめも
-rw-rw-r--  1 blacknon blacknon    0  1月 28 21:50 や ゆ よ
-rw-rw-r--  1 blacknon blacknon    0  1月 28 21:50 らりるれろ
-rw-rw-r--  1 blacknon blacknon    0  1月 28 21:50 わゐうゑを
-rw-rw-r--  1 blacknon blacknon    0  1月 28 21:50 ん

Q4.

「kiken」ディレクトリにある各ファイルについて、ファイル名とファイルの中身を入れ替えるという問題。 内1つが「2017/01/26(もう2018年だけど)」になっているので、ディレクトリを掘って対応してやる必要がある。

今まで知らなかったのだが、installコマンドを使う事で、「/」付きのPATHを指定したらディレクトリを掘って自動的にファイルを配置してくれるようだ。 これを利用することで、以下のように結構短く記述できる。

for i in *;do f=$(cat $i);install -m 644 -D <(echo $i) ./"$f";rm $i;done
blacknon@BS-PUB-UBUNTU-01:~/ShellGeiData/vol.33/kiken$ ls -al
合計 28
drwxrwxr-x 2 blacknon blacknon 4096  1月 28 22:24 .
drwxrwxr-x 4 blacknon blacknon 4096  1月 28 22:20 ..
-rw-rw-r-- 1 blacknon blacknon   20  1月 28 22:20 a
-rw-rw-r-- 1 blacknon blacknon   11  1月 28 22:20 b
-rw-rw-r-- 1 blacknon blacknon    2  1月 28 22:20 c
-rw-rw-r-- 1 blacknon blacknon    2  1月 28 22:20 d
-rw-rw-r-- 1 blacknon blacknon    3  1月 28 22:20 e
blacknon@BS-PUB-UBUNTU-01:~/ShellGeiData/vol.33/kiken$ grep '' ./*
./a:*                 *
./b:2017/01/26
./c:~
./d:-
./e:--
blacknon@BS-PUB-UBUNTU-01:~/ShellGeiData/vol.33/kiken$ for i in *;do f=$(cat $i);install -m 644 -D <(echo $i) ./"$f";rm $i;done
blacknon@BS-PUB-UBUNTU-01:~/ShellGeiData/vol.33/kiken$ ls -la
合計 28
-rw-r--r-- 1 blacknon blacknon    2  1月 28 22:24 *                 *
-rw-r--r-- 1 blacknon blacknon    2  1月 28 22:24 -
-rw-r--r-- 1 blacknon blacknon    2  1月 28 22:24 --
drwxrwxr-x 3 blacknon blacknon 4096  1月 28 22:24 .
drwxrwxr-x 4 blacknon blacknon 4096  1月 28 22:20 ..
drwxr-xr-x 3 blacknon blacknon 4096  1月 28 22:24 2017
-rw-r--r-- 1 blacknon blacknon    2  1月 28 22:24 ~
blacknon@BS-PUB-UBUNTU-01:~/ShellGeiData/vol.33/kiken$ grep -R '' ./*
./*                 *:a
./-:d
./--:e
./2017/01/26:b
./~:c

…なお、復習中に検証機の$HOME全部ふっ飛ばした。危ないので、消すときは「git clean -fd」でやったほうがいいようだ。
(ProxmoxでVMの日次バックアップ取っててよかった…あと検証機でやっててよかった)

Q5.

「yabai」ディレクトリにあるファイルの数を数えるという問題。 普通にlsを実行すると三行なのだが、ファイル名に改行を含んでいるためwc等でファイル数を数えると改行の数だけカウントが増えていってしまう。

blacknon@BS-PUB-UBUNTU-01:~/ShellGeiData/vol.33/yabai$ ls -la
合計 8
-rw-rw-r-- 1 blacknon blacknon    0  1月 28 22:20 ????
-rw-rw-r-- 1 blacknon blacknon    0  1月 28 22:20 ???????
-rw-rw-r-- 1 blacknon blacknon    0  1月 28 22:20 ????????????????????????????????
drwxrwxr-x 2 blacknon blacknon 4096  1月 28 22:20 .
drwxrwxr-x 4 blacknon blacknon 4096  1月 28 22:20 ..
blacknon@BS-PUB-UBUNTU-01:~/ShellGeiData/vol.33/yabai$ ls -1 | wc -l
46
blacknon@BS-PUB-UBUNTU-01:~/ShellGeiData/vol.33/yabai$ find ./ -type f
./????
./????????????????????????????????
./???????
blacknon@BS-PUB-UBUNTU-01:~/ShellGeiData/vol.33/yabai$ find ./ -type f | wc -l
46

これを対処するには、lsの-bオプション(エスケープを付与してファイル名を表示してくれる)を使用するといいようだ。

ls -1b | wc -l
blacknon@BS-PUB-UBUNTU-01:~/ShellGeiData/vol.33/yabai$ ls -1b | wc -l
3

個人的には、そんな行儀の悪いファイルを仕事で作られたら助走をつけてラリアットしてやりたいところだけど(´・ω・`)。 (lsにこういう機能があるってことは、今までにそういう事やった人が少なくなかったんだろうなぁ…)

Q6.

100万個のファイルを生成するという内容。 単純にseqで一気に数字を出してそれでファイルを作ればいいと思うけど、ファイル名にはひらがな、かたかな、漢字のみが使用できるという縛りがある。

最初はcrunch(ワードリスト生成用のコマンド)を使ってワードを生成すればいいかなと思ったのだけど、模範解答にあるsedのyコマンドで対応する方法の方が環境を選ばなそうだ。 (自分の中の中学二年生に従い、旧字体の漢数字にした)

seq 1000000 | sed 'y/0123456789/零壱弐参肆伍陸質捌玖/' | xargs -n1 touch

Q7.

Q6の応用で、lsを実行しても見えないファイルを100万個生成するという問題。 向かいの席の人がUTF-8のスペースを利用する(ココを見るとわかるけど、スペースだけでも複数定義されているようだ。なんでこんなにあるんだろう…?)というアプローチで対応していたので、同じやり方で解いてみた。

今回は0-9の10個分のスペースの種類があればいいので、以下のコードを使用した(厳密にはスペースじゃないのだけど、今回はひとまずそれは置いておく。一部はTeraterm等では対応していない文字コードなので注意)。

  • $'\u0009'
  • $'\u000D'
  • $'\u0020'
  • $'\u00A0'
  • $'\u2002'
  • $'\u2003'
  • $'\u2029'
  • $'\u3000'
  • $'\uFEFF'
  • $'\u180E'
seq 1000000 | sed "y/0123456789/$(echo $'\u0009\u000D\u0020\u00A0\u2002\u2003\u2029\u3000\uFEFF\u180E')/"  | xargs -n1 touch

Q8.

「yabai」ディレクトリの各ファイルに対し、ファイル名の改行の数を書き込むという内容。 ebanさんが速攻でシンプルな回答を出してきた。さすがだ…。bashの変数展開でファイル名の文字数を取得して、それをそのまま書き込んでいる。

for i in *;do echo ${#i} > "$i";done

今回は正直難しかった…(´・ω・`) (毎回言ってる気がする)