グロブの展開

30分プログラム、その176。[n-m]という形のグロブを展開する。

例えば、zshだと

$ rm tmp[0-2]

とやるとtmp0、tmp1、tmp2がまとめて削除できる。

このような文字列の展開をSchemeでやってみる。

使い方

gosh> (expand-glob "a[0-3].txt")
("a0.txt" "a1.txt" "a2.txt" "a3.txt")

;; 最初に0がついている場合はそれを保持する
gosh> (expand-glob "a[00-03].txt")
("a00.txt" "a01.txt" "a02.txt" "a03.txt")

;; 複数[n-m]がある場合は両方展開する
;; 先に登場したものが先に展開される
gosh> (expand-glob "a[0-2][0-2].txt")
("a00.txt" "a01.txt" "a02.txt" 
 "a10.txt" "a11.txt" "a12.txt"
 "a20.txt" "a21.txt" "a22.txt")
gosh> 

ソースコード

#! /opt/local/bin/gosh
;; -*- mode:scheme; coding:utf-8 -*-
;;
;; glob_expand.scm -
;;
;; Copyright(C) 2007 by mzp
;; Author: MIZUNO Hiroki <hiroki1124@gmail.com> 
;; http://mzp.sakura.ne.jp/
;;
;; Timestamp: 2007/11/07 20:31:21
;;
;; This program is free software; you can redistribute it and/or
;; modify it under the same terms as Scheme itself.
;;
(use srfi-1)

;; 文字列版のrange。文字列の幅を指定できる
;;
;; e.g. (range 0 3 1) -> '("0" "1" "2" "3")
;; e.g. (range 0 3 2) -> '("00" "01" "02" "03")
(define (range from to width)
  ;; format用の文字列を生成する
  (let1 f (format #f "~~~d,'0d" width) ; e.g. "~3,'0d"
    (map (pa$ format #f f)
	 (iota (- to from -1) from))))

;; 文字列中の[n-m]を展開する
(define (expand-glob str)
  (let1 m (rxmatch #/\[(\d+)-(\d+)\]/ str)
    (if m
	(let ((from  (rxmatch-substring m 1))
	      (to    (rxmatch-substring m 2))
	      ;; 後続の文字列の[n-m]を展開しておく
	      (after (expand-glob (rxmatch-after m))))
	  (append-map
	   (lambda (n) (map (pa$ string-append (rxmatch-before m) n)
			    after))
	   (range (string->number from)
		  (string->number to)
		  (string-length from))))
	(list str))))