概要

先日、「第9回寒中シェル芸勉強会&第27回バナナで釘が打てますUSP友の会定例会」(http://usptomo.doorkeeper.jp/events/8339)にリモートで参加しまして。
https://twitter.com/kunst1080/status/434556029561344000
当日は普通にFreeBSD上のzshとかbashとかでやりましたが、今日はその問題をWindows7上のコマンドプロンプト(cmd.exe)でやってみようと思います。

資料

コンセプト

今回の問題は、ファイル操作系がメインでした。ファイル操作はcmdでも普段から割とやる感じです。
普段のノリでcmd叩けばよし。詰んだらワンライナーにこだわらずさっくり!の心構えでいきましょう。※cmd.exeはコマンド拡張機能・遅延環境変数の展開は有効にしておくこと。(/V オプションを付けて起動)

第1問

ファイルを、先頭1文字目の名前のディレクトリへ移動する

 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
rem 1ファイルずつ作成。
copy nul apple
copy nul avocado
copy nul banana
copy nul cinnamon
copy nul melon
mkdir a
mkdir b
mkdir c
mkdir m

rem echoでコマンドを確認
for /f "usebackq tokens=*" %a in (`dir /a-d /b`) do @(set X=%a && echo move !x! !X:~0,1!)

rem echoを外して実行!
for /f "usebackq tokens=*" %a in (`dir /a-d /b`) do @(set X=%a && move !x! !X:~0,1!)

rem 結果確認
dir /s /b
  C:\usr\workspace\cmd\20140215\Q1\a
  C:\usr\workspace\cmd\20140215\Q1\b
  C:\usr\workspace\cmd\20140215\Q1\c
  C:\usr\workspace\cmd\20140215\Q1\m
  C:\usr\workspace\cmd\20140215\Q1\a\apple
  C:\usr\workspace\cmd\20140215\Q1\a\avocado
  C:\usr\workspace\cmd\20140215\Q1\b\banana
  C:\usr\workspace\cmd\20140215\Q1\c\cinnamon
  C:\usr\workspace\cmd\20140215\Q1\m\melon

第2問

ファイル名に含まれる空白をアンダーバーにしてリネームする

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
rem ファイル作るよ!
copy nul "私は 蟹"
copy nul "オシャレな 蟹"
copy nul "足が 10本"

rem echoでコマンドを確認
for /f "usebackq tokens=*" %a in (`dir /a-d /b`) do @(set X=%a&& echo ren "!x!" "!X: =_!")

rem echoを外して実行!
for /f "usebackq tokens=*" %a in (`dir /a-d /b`) do @(set X=%a&& ren "!x!" "!X: =_!")

rem 結果確認
dir /b
  オシャレな_蟹
  私は_蟹
  足が_10本

問1とだいたい同じですね。

第3問

ディレクトリを適当に作って、20140101から20141231まで、日付に対応したファイルを作成。
フィアル内にはdateコマンドの実行結果を入れる。普通にやるのは無理なので、有効な日付を判定するコマンドを適当に作成しておきます。isDate.bat


1
2
3
4
5
6
7
8
9
rem コマンドを確認
for /L %a in (20140101,1,20141231) do @(call isdate %a && echo %a)

rem リダイレクトを付けて実行!
for /L %a in (20140101,1,20141231) do @(call isdate %a && echo %a>%a)

rem 結果確認
dir /s /b

※処理超遅い>< のと、ファイルの中身は手抜き。

第4問

ファイルの中身を入れ替え

 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
rem ファイル作るよ!
echo カレー > a_ramen
echo ラーメン > a_cury
echo トマト > b_apple
echo りんご > b_tomato

rem (余談)UNIXではgrepを使いましたが、コマンドプロンプトではfindstrでファイルと中身を並べて確認できます。
findstr . *
  a_cury:ラーメン
  a_ramen:カレー
  b_apple:トマト
  b_tomato:りんご

rem echoでコマンドを確認
(dir /w a_* && dir /w b_*) | findstr "a_ b_" | for /f "usebackq tokens=1,2" %a in (`findstr .*`) do @echo ren %a tmp && echo ren %b %a && echo ren tmp %b
rem echoを外して実行!
(dir /w a_* && dir /w b_*) | findstr "a_ b_" | for /f "usebackq tokens=1,2" %a in (`findstr .*`) do @ren %a tmp && ren %b %a && ren tmp %b

rem 結果確認
findstr . *
  a_cury:カレー
  a_ramen:ラーメン
  b_apple:りんご
  b_tomato:トマト

第5問

問3のファイルを各月ごとにtar.gzファイルにするtar.gzとか無理っす。
とりえずフォルダに分けときまーす

 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
rem echoでコマンドを確認
for /f "usebackq tokens=*" %a in (`dir /b 2014*`) do @(set X=%a&& echo if not exist !X:~0,6! mkdir !X:~0,6! && echo move %a !X:~0,6!)

rem echoを外して実行!
for /f "usebackq tokens=*" %a in (`dir /b 2014*`) do @(set X=%a&& if not exist !X:~0,6! mkdir !X:~0,6! && move %a !X:~0,6!)

rem エラー!?ちょっと構文を買えてやりなおし
for /f "usebackq tokens=*" %a in (`dir /b 2014*`) do @(set X=%a&& echo if not exist !X:~0,6! mkdir !X:~0,6! && echo move %a !X:~0,6!)>> exec.bat && @cmd /c exec.bat

rem なんかエラー出るけどファイルは移動するorz

rem 結果確認
tree /f
  フォルダー パスの一覧
  ボリューム シリアル番号は 3AC0-14F1 です
  C:.
  │  exec.bat
  │
  ├─201401
  │      20140101
  │      20140102
  │      20140103
  │      20140104
以下略!

第6問

mkdir -p jot -s / -b a 100 && touch jot -s / -b a 100/b && cd jot -s / -b a 100 するやつ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
rem 3階層くらいで実験
(for /L %a in (1,1,3) do mkdir a && cd a) && copy nul b
cd ..\..\..
rd /s a

rem 一気に実行!
(for /L %a in (1,1,100) do mkdir a && cd a) && copy nul b

rem 結果確認
echo %CD%
 C:\usr\workspace\cmd\20140215\Q6\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a

結果確認その2。
とりあえず、標準入力を指定文字で分割するスクリプトを作っておいた。sepa.bat

これを使って、aディレクトリの数を以下のように確認できる。
1
2
3
echo %CD% | sepa "\\"
echo %CD% | sepa "\\" | find /c "a"

第7問

先ほど作ったファイルbを、50番目のaディレクトリに移動する。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
rem とりあえず、元のディレクトリに戻ってから。
rem ディレクトリ名を作成できるか実験
set X1= && (for /L %a in (1,1,100) do @set X1=!X1!a\) && echo !X1!

rem いけそうなので実行。ただし、面倒なので分割実行。
set X1= && (for /L %a in (1,1,100) do @set X1=!X1!a\) && echo !X1!
set X2= && (for /L %a in (1,1,50)  do @set X2=!X2!a\) && echo !X2!
move %X1%b %X2%b

rem 結果確認
dir /s /b b
dir /s /b b | sepa "\\" | find /c "a"

第8問

先ほど作ったディレクトリについて、rm -rを使わずに~/a以下のディレクトリを消去してください。rm -r じゃなくて rd /s /q ・・・という冗談はさておき。delとrmdirで消していきます。

1
2
for /L %a in (1,1,99) do @cd a && for /L %a in (1,1,100) do @(rmdir a && cd .. && if exist b del b)

なぜか「ディレクトリが空ではありません。」というエラーが出るが、実際には空なのでディレクトリはちゃんと削除されてる。
一応、最後まで普通にやれました。