seq を真面目に実装してみる
ref:技術メモ帳 - seq コマンド for BSD
ref:メモ帳 - my bashrc
代わりに、ものすごく高機能な jot コマンドというのが入っているので
それを使って seq を実装してみた。function seq(){ jot $2 $1 $2 }
ええと、$1 が 1 以外のときにうまく動かないと思うなぁ。しかも、-f も -w も使えないのはちょっとつらいかなぁ。
話の元ネタがこれ。
function rseq() { ruby -e '((ARGV[0].to_i)..(ARGV[1].to_i)).each {|x| printf "%0#{ARGV[0].length}d\n", x }' $1 $2 }
zero padding しようとして失敗しているのが面白いところであります。試さなかったんでしょうか。 第1引数を 001 とかにすれば zero padding されますね。すみません。
というかですね、.bashrc とか .zshrc に shell function として書いたとしても shell script からは使えなくてはとても悲しいと思うのですが、どうでしょう。あとみなさん "$1" "$2" みたいに引数を double quote で囲ったりしないんでしょうか*1。
つうわけで、とりあえず
- -wオプションを受け付ける
- 取り扱いは整数の範囲
- step は正のみ。(count down は非対応)
なんて仕様で、Python で書いてみる。エラー処理とかない適当実装。
#!/usr/bin/env python import sys import getopt try: (options, args) = getopt.getopt(sys.argv[1:], "w") except getopt.GetoptError, e: print >>sys.stderr, e.msg sys.exit(1) if len(options) > 0: equal_width = True else: equal_width = False first, step = 1, 1 if len(args) == 1: last = int(args[0]) elif len(args) == 2: first, last = map(int, args) elif len(args) == 3: first, last, step = map(int, args) if equal_width: width = len(str(last)) format = "%0" + str(width) + "d" else: format = "%d" for i in range(first, last + 1, step): print format % i
調子に乗って、負の step とか -s オプション、 -f オプションにも対応したのを getopts の練習がてら bash script で書いてみる。
#!/bin/bash fill_zero= sep=$'\n' format= start=1 step=1 while getopts "ws:f:" option do case $option in w) fill_zero=1 ;; s) sep="$OPTARG" ;; f) format="$OPTARG" ;; esac done [ $OPTIND > 1 ] && shift $((OPTIND - 1)) case $# in 0) echo "$0: missing operand" 1>&2 exit 1 ;; 1) end=$1 ;; 2) start=$1 end=$2 ;; 3) start=$1 step=$2 end=$3 ;; esac if [ -n "$format" -a -n "$fill_zero" ] then echo "$0: format string may not be specified when printing equal width strings" 1>&2 exit 1 fi if [ -n "$fill_zero" ] then width=$(printf '%g' $end | wc -c | tr -d ' ') format="%0${width}g" fi [ -z "$format" ] && format="%g" if [ $step -ge 0 ] then op=-le else op=-ge fi if [ ! $start $op $end ] then exit fi i="$start" while true do printf "$format" $i i=$((i + step)) if [ $i $op $end ] then echo -n "$sep" else echo break fi done
なげぇ。
で、さらにまじめに long options にも対応したのをまた Python で。
#!/usr/bin/env python import operator def seq(start, last, step): if step > 0: op = operator.le else: op = operator.ge i = start while op(i, last): yield i i += step if __name__ == '__main__': import sys from optparse import OptionParser step = 1 first = 1 parser = OptionParser() parser.add_option("-w", "--equal-width", action="store_true", dest="equal_width", default=False) parser.add_option("-f", "--format", dest="format", default="%g") parser.add_option("-s", "--separator", dest="separator", default="\n") (options, args) = parser.parse_args() if len(args) == 1: last = int(args[0]) elif len(args) == 2: first, last = map(int, args) elif len(args) == 3: first, step, last = map(int, args) elif len(args) > 4: parser.print_usage() sys.exit(1) else: parser.print_usage() sys.exit(1) sep = options.separator format = options.format if options.equal_width: width = max(len(str(first)), len(str(last))) format = "%0" + str(width) + "d" print sep.join(format % i for i in seq(first, last, step))
追記
wc -c の結果に空白が入る環境があるようなので tr を追加。thanks id:lurker さん。