トップ 最新の日記 ユーザー登録 ログイン ヘルプ

のびのびなRuby日記 このページをアンテナに追加 RSSフィード

2007-06-23

CheriHello World! CheriでHello World! - のびのびなRuby日記 を含むブックマーク はてなブックマーク - CheriでHello World! - のびのびなRuby日記 CheriでHello World! - のびのびなRuby日記 のブックマークコメント

CheriというJRuby用のGUIフレームワークを使ってみました。因にCheriは元々JRBuilderという名前だったらしいです。

前提条件

JRubyが必要です。JRubyインストールされていない方はJRubyインストールして下さい。

JRubyインストールに関しては、こちらをご参照下さい。

Cheriインストール

以下のコマンドを実行してcheriインストールします。

jruby -S gem install cheri

simple_hello_world.rb

#simple_hello_world.rb
require 'rubygems'
require 'cheri/swing'
include Cheri::Swing

#swingのframeを生成。ブロックで渡した文字列がframeのボックス(?)に表示されます。
@frame = swing.frame('Hello World!') { label 'Hello Ruby!'} 
@frame.visible = true

実行例

jruby simple_hello_world.rb

f:id:NobiNobiKota:20070624183905j:image

little_bit_complex_hello_world.rb

今度は少し複雑なHelloWorldのサンプルを作ってみました。

#little_bit_complex_hello_world.rb
require 'rubygems'
require 'cheri/swing'
include Cheri::Swing
@frame = swing.frame('Hello World!') do
  #frameのサイズを指定
  size 180, 180
 #frameのボックス内にClick me!というボタンを付けて、そのボタンを押すとframe(画面)が終了します。
  button('Click me!') do
    on_click{@frame.dispose}
  end
end

@frame.visible = true
実行例

jruby little_bit_complex_hello_world.rb

f:id:NobiNobiKota:20070624185903j:image

Complex_hello_world.rb

先ほどより更に複雑なHelloWorldを書いてみました。

require 'rubygems'
require 'cheri/swing'
include Cheri::Swing

@frame = swing.frame('Hello World!') do 
  size 200, 200
  flow_layout
  #親フレームのxボタンを押すと親フレームが終了します。
  on_window_closing {|event| @frame.dispose}
  #親フレームにあるClick me!ボタンをクリックすると子フレームが生成されます。
  button('Click me!') do
    on_click do
      @child_frame = swing.frame('Child Frame') do
        size 200, 200
        #子フレームにはHit me!ボタンが付いていて、それをクリックすると子フレームが終了します。
        button('Hit me!') do
          on_click do
            @child_frame.dispose
          end
        end
      end
        @child_frame.visible = true  
    end
  end
end

@frame.visible = true
実行例

jruby complex_hello_world.rb

f:id:NobiNobiKota:20070624191146j:image

f:id:NobiNobiKota:20070624191203j:image

2007-06-22

JRuby その1  JRuby その1 - のびのびなRuby日記 を含むブックマーク はてなブックマーク -  JRuby その1 - のびのびなRuby日記  JRuby その1 - のびのびなRuby日記 のブックマークコメント

JavaクラスFromJavaToRubyから、RubyクラスGreetingServiceで定義されたインスタンスメソッドgreetを呼ぶサンプルコードを書いてみました。

必要なファイルディレクトリ構成

|-- ruby
|   `-- greeting_service.rb
`-- src
    `-- example
        `-- FromJavaToRuby.java
class GreetingService
  def greet
    puts "Hello, World!"
  end
end

#下の一行を書いておかないとruby.evalScript(script)の結果(評価結果)がnullになり実行時エラーが起きます。
GreetingService.new
package example;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

import org.jruby.Ruby;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;

public class FromJavaToRuby {
	public static void main(String[] args) throws IOException {
		String script = loadRubyScript("ruby/greeting_service.rb");
		Ruby ruby = Ruby.getDefaultInstance();

		// greeting_service.rbの内容を評価
		IRubyObject ir = ruby.evalScript(script);

		// GreetingServiceクラスのインスタンスに対してgreetを実行
		ThreadContext threadContext = Ruby.getDefaultInstance()
				.getCurrentContext();
		IRubyObject result = ir.callMethod(threadContext, "greet");

		// greetメソッドを呼んだ結果を表示
		System.out.println(result.toString());
	}

	/**
	 * 引数で渡されたRubyのスクリプトを読み込むメソッド
	 * 
	 * @param filePath
	 * @return
	 * @throws IOException
	 */
	private static String loadRubyScript(String filePath) throws IOException {
		FileInputStream fis = new FileInputStream(filePath);
		InputStreamReader is = new InputStreamReader(fis);
		BufferedReader br = new BufferedReader(is);

		// 1行読み込む毎に改行コードを入れる必要があります
		StringBuffer buffer = new StringBuffer();
		for (String line = br.readLine(); line != null;) {
			buffer.append(line);
			buffer.append("¥n");
			line = br.readLine();
		}
		return buffer.toString();
	}
}

実行結果

Hello, World!

2007-06-16

メタプログラミング その3  メタプログラミング その3 - のびのびなRuby日記 を含むブックマーク はてなブックマーク -  メタプログラミング その3 - のびのびなRuby日記  メタプログラミング その3 - のびのびなRuby日記 のブックマークコメント

instance_evalを使うと、インスタンス毎にインスタンスメソッドを追加出来ますが、インスタンス毎にインスタンスメソッドを定義出来ることに、何のメリットがあるのかが分かりません。

class Employee
  #fooメソッドを呼ぶと、barメソッド(インスタンスメソッド)が定義される。
  def foo
    instance_eval %Q{
      def bar
        puts "You are calling bar method"
      end
    }
  end
end

実行例

irb(main):001:0> load 'employee.rb'
=> true

Employeeクラスのインスタンスを生成
irb(main):002:0> emp1 = Employee.new
=> #<Employee:0x701ac>

#emp1に対してfooメソッドを実行したので、emp1にはbarというインスタンスメソッドが追加で定義された。
irb(main):003:0> emp1.foo
=> nil

#emp1はbarメソッドが定義されているので、barメソッドを呼ぶとメッセージが表示される。
irb(main):004:0> emp1.bar
You are calling bar method
=> nil

#Employeeクラスのインスタンスを別途生成
irb(main):005:0> emp2 = Employee.new
=> #<Employee:0x60be4>

#emp2はfooメソッドを呼んでないので、barメソッドは定義されない。従ってemp2に対してbarメソッドを実行すると、そんなメソッドはないと怒られる。
irb(main):006:0> emp2.bar
NoMethodError: undefined method `bar' for #<Employee:0x60be4> from (irb):6

RubyKaigi2007  RubyKaigi2007 - のびのびなRuby日記 を含むブックマーク はてなブックマーク -  RubyKaigi2007 - のびのびなRuby日記  RubyKaigi2007 - のびのびなRuby日記 のブックマークコメント

丁度一週間前ですが今年もRubyKaigiへ参加してきました。Dave Thomasさんの話にはRubyに対する愛が満ちあふれていた。凄く良かったです。またDaveさんにAgile Web Development with Rails(以下AWDwRと略) と達人プログラマサインして頂きました。

特にAWDwRには昨年DHHにサインして頂き、そして今年はDaveさんにサインして頂いたので、著者二人のサインを二年越しで獲得出来たのは凄く嬉しいです。

RubyKaigiスタッフの皆様、お疲れさまでした。また来年楽しみにしております。

メタプログラミング その2  メタプログラミング その2 - のびのびなRuby日記 を含むブックマーク はてなブックマーク -  メタプログラミング その2 - のびのびなRuby日記  メタプログラミング その2 - のびのびなRuby日記 のブックマークコメント

今回の例も実用性ありませんが、生成したオブジェクトに対して動的にアクセサとインスタンスフィールドを追加出来るメソッドdefine_accessorを定義してみました。

class Song
  def initialize(name, artist, duration)
    @name = name
    @artist = artist
    @duration = duration
  end

  def define_accessor(*syms)
      syms.each do |sym|
        instance_eval %Q{
          def #{sym}
            return @#{sym}
          end
          def #{sym}=(val)
            @#{sym}=val
          end
        } 
      end
  end
end 

実行例

irb(main):001:0> load 'song.rb'
=> true

irb(main):002:0> s = Song.new("Snow Smile", "Bump Of Chiken", 360)
=> #<Song:0x67570 @duration=360, @name="Snow Smile", @artist="Bump Of Chiken">

#Songクラスのオブジェクトsにname,artist,durationというアクセサが定義されていないことを確認
irb(main):003:0> s.methods.each do |method|
irb(main):004:1*   if /^(name|artist|duration)$/ =~ method
irb(main):005:2>     puts "#{method} method found"
irb(main):006:2>   end
irb(main):007:1> end
=> ["instance_variables", "__id__", "to_s", "send", "dup", "private_methods", "display", "=~", "is_a?", "class", "tainted?", "singleton_methods", "eql?", "untaint", "instance_of?", "method", "id", "instance_variable_get", "inspect", "instance_eval", "extend", "nil?", "__send__", "frozen?", "taint", "object_id", "instance_variable_defined?", "public_methods", "hash", "to_a", "clone", "protected_methods", "respond_to?", "freeze", "kind_of?", "==", "instance_variable_set", "type", "===", "equal?", "methods", "define_accessor"]

#sに対して動的にname,artist,durationというアクセサを定義
irb(main):008:0> s.define_accessor(:name, :artist, :duration)
=> [:name, :artist, :duration]

#再度sにname,artist,durationというアクセサが定義されているかどうか確認。今度は3つのアクセサが定義されていることが分かります。
irb(main):009:0> s.methods.each do |method|
irb(main):010:1*   if /^(name|artist|duration)$/ =~ method
irb(main):011:2>     puts "#{method} method found"
irb(main):012:2>   end
irb(main):013:1> end
name method found
artist method found
duration method found
=> ["inspect", "clone", "method", "public_methods", "instance_variable_defined?", "equal?", "freeze", "methods", "respond_to?", "dup", "instance_variables", "__id__", "object_id", "eql?", "id", "singleton_methods", "send", "taint", "define_accessor", "frozen?", "instance_variable_get", "__send__", "instance_of?", "to_a", "name", "type", "protected_methods", "instance_eval", "name=", "artist", "==", "display", "===", "instance_variable_set", "artist=", "kind_of?", "extend", "to_s", "hash", "class", "duration", "tainted?", "=~", "private_methods", "nil?", "untaint", "duration=", "is_a?"]
irb(main):014:0> s.instance_variables
=> ["@duration", "@name", "@artist"]

#sに対して動的にgenreアクセサとインスタンスフィールドを追加
irb(main):015:0> s.define_accessor(:genre)
=> [:genre]

#sのインスタンスフィールドを確認するが、まだgenreフィールドに値を設定していないのでduration,name,artistというインスタスフィールドしか表示されない。
irb(main):016:0> s.instance_variables
=> ["@duration", "@name", "@artist"]

#sに新しく追加したgenreというインスタンスフィールドに値を設定
irb(main):017:0> s.genre = "J-Pop"
=> "J-Pop"

#再度sのインスタンスフィールドを確認。今度はgenreというインスタンスフィールドが追加されています。
irb(main):018:0> s.instance_variables
=> ["@genre", "@duration", "@name", "@artist"]


#sにname,artist,duration,genreというアクセサがあるかどうか確認してみます。4つ共アクセサとして存在していることが確認できました。
irb(main):019:0> s.methods.each do |method|
irb(main):020:1*   if /^(name|artist|duration|genre)$/ =~ method
irb(main):021:2>     puts "#{method} method found"
irb(main):022:2>   end
irb(main):023:1> end
name method found
artist method found
duration method found
genre method found
=> ["inspect", "genre=", "clone", "method", "public_methods", "instance_variable_defined?", "equal?", "freeze", "methods", "respond_to?", "dup", "instance_variables", "__id__", "object_id", "eql?", "id", "singleton_methods", "send", "taint", "define_accessor", "frozen?", "instance_variable_get", "__send__", "instance_of?", "to_a", "name", "type", "protected_methods", "instance_eval", "name=", "artist", "==", "display", "===", "instance_variable_set", "artist=", "kind_of?", "extend", "to_s", "hash", "class", "duration", "tainted?", "=~", "private_methods", "nil?", "untaint", "duration=", "genre", "is_a?"]

#先ほどとは異なるSongクラスのオブジェクトを生成
irb(main):024:0> s1 = Song.new("Whatever", "Oasis", 300)
=> #<Song:0x44444 @duration=300, @name="Whatever", @artist="Oasis">

#s1のインスタンスフィールドを確認すると、duration,name,artistはあるが、genreはないことが確認されます。       
irb(main):025:0> s1.instance_variables
=> ["@duration", "@name", "@artist"]

#s1にname,artist,duration,genreというアクセサが定義されているか確認。どのアクセサも定義されていないことが分かります。
irb(main):026:0> s1.methods.each do |method|
irb(main):027:1*   if /^(name|artist|duration|genre)$/ =~ method
irb(main):028:2>     puts "#{method} method found"
irb(main):029:2>   end
irb(main):030:1> end
=> ["instance_variables", "__id__", "to_s", "send", "dup", "private_methods", "display", "=~", "is_a?", "class", "tainted?", "singleton_methods", "eql?", "untaint", "instance_of?", "method", "id", "instance_variable_get", "inspect", "instance_eval", "extend", "nil?", "__send__", "frozen?", "taint", "object_id", "instance_variable_defined?", "public_methods", "hash", "to_a", "clone", "protected_methods", "respond_to?", "freeze", "kind_of?", "==", "instance_variable_set", "type", "===", "equal?", "methods", "define_accessor"]

メタプログラミング その1  メタプログラミング その1 - のびのびなRuby日記 を含むブックマーク はてなブックマーク -  メタプログラミング その1 - のびのびなRuby日記  メタプログラミング その1 - のびのびなRuby日記 のブックマークコメント

まつもと直伝 プログラミングのオキテ 第6回を参考にしてメタプログラミングの練習をしてみました。

全然実用性はないのですが、クラス内にsetter :nameやgetter :nameと書いておくと、setNameで@nameに値をセット出来て、getNameで@nameの値を取得出来るようになります。

#ファイル名:module.rb
class Module
  def getter(*syms)
    syms.each do |sym|
      class_eval %{
        def get#{sym.to_s.capitalize}
          return @#{sym}
        end
      }
    end
  end

  def setter(*syms)
    syms.each do |sym|
      class_eval %{
        def set#{sym.to_s.capitalize}=(val)
          @#{sym}=val
        end
      }
    end
  end
end

実行例

irb(main):001:0> load 'module.rb'
=> true
irb(main):002:0> class Song
irb(main):003:1>   setter :name, :artist, :duration
irb(main):004:1>   getter :name, :artist, :duration
irb(main):005:1> end
=> [:name, :artist, :duration]
irb(main):006:0> song = Song.new
=> #<Song:0x5bb1c>
irb(main):007:0> song.setName="Wherever You Will Go"
=> "Wherever You Will Go"
irb(main):008:0> song.setArtist="The Calling"
=> "The Calling"
irb(main):009:0> song.setDuration="204"
=> "204"
irb(main):010:0> puts "Name: #{song.getName} / Artist: #{song.getArtist} / Duration: #{song.getDuration}"
Name: Wherever You Will Go / Artist: The Calling / Duration: 204

2007-05-30

ヒアドキュメントの練習 ヒアドキュメントの練習 - のびのびなRuby日記 を含むブックマーク はてなブックマーク - ヒアドキュメントの練習 - のびのびなRuby日記 ヒアドキュメントの練習 - のびのびなRuby日記 のブックマークコメント

ヒアドキュメントの練習です。以下スクリプトを実行すると、ヒアドキュメント内のXML文書を内容にもつmovie.xmlファイルが新規作成されます。

#!/usr/bin/env ruby
#EOFで始まりEOFで終わるヒアドキュメントの内容をMOVIE_DOC定数に代入します。
MOVIE_DOC = <<-EOF
<?xml version="1.0" encoding="Shift_JIS"?>
<movies>
  <movie title="Music and Lyrics">
    <release_date>2007/02/14</release_date>
    <cast>
      <actor>Hugh Grant</actor>
      <actor>Drew Barrymore</actor>
    </cast>
    <genre>comedy</genre>
  </movie>
  <movie title="Fever Pitch">
    <release_date>2005/04/08</release_date>
    <cast>
      <actor>Drew Barrymore</actor>
      <actor>Jimmy Fallon</actor>
    </cast>
    <genre>comedy</genre>
  </movie>
  <movie title="50 First Dates">
    <release_date>2004/02/13</release_date>
    <cast>
      <actor>Adam Sandler</actor>
      <actor>Drew Barrymore</actor>
    </cast>
    <genre>comedy</genre>
  </movie>
  <movie title="The Wedding Singer">
    <release_date>1998/02/13</release_date>
    <cast>
      <actor>Adam Sandler</actor>
      <actor>Drew Barrymore</actor>
    </cast>
    <genre>comedy</genre>
  </movie>
  <movie title="Eternal Sunshine of the Spotless Mind">
    <release_date>2004/03/19</release_date>
    <cast>
      <actor>Jim Carry</actor>
      <actor>Kate Winslet</actor>
    </cast>
    <genre>Romance</genre>
  </movie>
</movies>
EOF

#movie.xmlを書き込み専用でファイルを開きます。
File.open("movie.xml", "w") do |file|
#movie.xmlにMOVIE_DOCの内容を書き込みます。openメソッドにブロックを渡しているので、書き込みが終わって時点で、ファイルがクローズされます(closeのし忘れなし)
  file.puts MOVIE_DOC
end

2007-05-29

サーブレットに必要なフォルダファイルを作るスクリプト  サーブレットに必要なフォルダやファイルを作るスクリプト - のびのびなRuby日記 を含むブックマーク はてなブックマーク -  サーブレットに必要なフォルダやファイルを作るスクリプト - のびのびなRuby日記  サーブレットに必要なフォルダやファイルを作るスクリプト - のびのびなRuby日記 のブックマークコメント

最近社内研修Servletを教えているのですが、自宅でviを使いコンテキストを新規作成する度に同じような作業を繰り返しているのが面倒になったのでRubyスクリプトを書き自動化しました。

因に職場では、Eclipseを使ってJavaコードを書いていますが、自宅ではどの言語コードも大抵はviコードを書いています。

下記のディレクトリのcontex_name以下のディレクトリと、build.xml及びweb.xmlファイルを作成され、libフォルダservlet-api.jarコピーされます。

$CATALINA_HOME|
              +webapps
                |
                +context_name
                   |
                   +build.xml
                   |
                   +META-INF
                   |   |
                   |   +-context.xml
                   |
                   +WEB-INF
                     |
                     +-web.xml
                     |
                     +classes
                     |
                     +src
                     |
                     +lib

build.xml及びweb.xmlには以下の雛形が書き込まれます。

<!-- build.xml -->
<?xml version="1.0" encoding="Shift_JIS"?>
<project default="compile">
  <property name="src" location="./WEB-INF/src" />
  <property name="lib" location="./WEB-INF/lib" />
  <property name="output" location="./WEB-INF/classes" />
  <target name="init">
    <mkdir dir="${output}" />
  </target>
  <target name="compile" depends="init">
    <javac 
      srcdir="${src}"
      destdir="${output}" 
      classpath="${lib}/servlet-api.jar" />
  </target>
  <target name="clean">
    <delete dir="${output}" />
  </target>
</project>
<!-- web.xml -->
<?xml version="1.0" encoding="Shift_JIS"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
    version="2.4">
  <display-name>Template</display-name>
  <description>
    Template
  </description>
  <servlet>
    <servlet-name>DummyServlet</servlet-name>
    <servlet-class>DummyServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>DummyServlet</servlet-name>
    <url-pattern>/DummyServlet</url-pattern>
  </servlet-mapping>
</web-app>

適当ディレクトリにcontext_template_builder.rbと mk_context.rbを保存してmk_context.rb basicのように実行すると

$CATALINA_HOME/webapps/basicが出来ています。

#Windows環境で正常に実行されるかどうかは分かりません(Mac環境では動きました)。それと自分用に作ったスクリプトですので、問題があっても一切責任とれません(^^;

require 'fileutils'

class ContextTemplateBuilder
  def make_dir(context_name)
    file_list = make_dir_path(context_name)
    file_list.each do |file_path|
      begin 
        Dir.mkdir(file_path)
      rescue Errno::EEXIST
        puts "#{file_path} directory already existed!"
      end
    end
  end

  def make_file(hash)
    hash.each do |file_path, here_doc|
      if (!file_exist?(file_path))
        File.open(file_path, "w") do |file|
          file.puts here_doc
        end
      end
    end
  end

  def copy_file(from, to)
    FileUtils.cp(from, to)
  end

  private
  def make_dir_path(context_name)
    file_list = Array.new
    catalina_home = ENV['CATALINA_HOME'] || ENV['TOMCAT_HOME']
    context_path = catalina_home + '/webapps/' + context_name
    web_inf_dir = context_path + '/WEB-INF/'
    meta_inf_dir = context_path + '/META-INF/'
    class_dir = web_inf_dir + 'classes'
    src_dir = web_inf_dir + 'src'
    lib_dir = web_inf_dir + 'lib'
    file_list << context_path
    file_list << web_inf_dir << meta_inf_dir << class_dir
    file_list << src_dir << lib_dir
    return file_list
  end

  def file_exist?(file_path)
    begin 
      File.open(file_path, "r") {|file|}
      return true
    rescue Errno::ENOENT
      return false
    end
  end
end
#!/usr/bin/env ruby

require 'fileutils'
require 'context_template_builder'

WEB_XML_HERE_DOC = <<-EOF
<?xml version="1.0" encoding="Shift_JIS"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
    version="2.4">
  <display-name>Template</display-name>
  <description>
    Template
  </description>
  <servlet>
    <servlet-name>DummyServlet</servlet-name>
    <servlet-class>DummyServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>DummyServlet</servlet-name>
    <url-pattern>/DummyServlet</url-pattern>
  </servlet-mapping>
</web-app>
EOF

BUILD_XML_HERE_DOC = <<-EOF
<?xml version="1.0" encoding="Shift_JIS"?>
<project default="compile">
  <property name="src" location="./WEB-INF/src" />
  <property name="lib" location="./WEB-INF/lib" />
  <property name="output" location="./WEB-INF/classes" />
  <target name="init">
    <mkdir dir="${output}" />
  </target>
  <target name="compile" depends="init">
    <javac 
      srcdir="${src}"
      destdir="${output}" 
      classpath="${lib}/servlet-api.jar" />
  </target>
  <target name="clean">
    <delete dir="${output}" />
  </target>
</project>
EOF

CONTEXT_XML_HERE_DOC = <<-EOF
<Context reloadable="true"/>
EOF

if (ARGV.length != 1)
  puts "コンテキスト名を一つ指定して下さい。"
  puts "(実行例): mk_context.rb basic"
  exit
end 

CATALINA_HOME = ENV['CATALINA_HOME'] || ENV['TOMCAT_HOME']
LIB_DIR = ARGV[0] + "/WEB-INF/lib"
API_FILE_PATH = CATALINA_HOME + "/common/lib"
CONTEXT_PATH = CATALINA_HOME + '/webapps/' + ARGV[0]
WEB_XML = CONTEXT_PATH + '/WEB-INF/web.xml'
BUILD_XML = CONTEXT_PATH + '/build.xml'
CONTEXT_XML = CONTEXT_PATH + '/META-INF/context.xml'
hash = {WEB_XML => WEB_XML_HERE_DOC, BUILD_XML => BUILD_XML_HERE_DOC, CONTEXT_XML => CONTEXT_XML_HERE_DOC}

s = ContextTemplateBuilder.new
s.make_dir(ARGV[0])
s.make_file(hash)
s.copy_file("#{API_FILE_PATH}/servlet-api.jar", LIB_DIR)
puts "#{ARGV[0]} コンテキストが作成されました。"

作成後の感想は、単純な作業を行うスクリプトの割にはコードが長くなってしまったなということでしょうか。

単にShell Scriptで書いた方がもっと簡潔に書けるのでしょうか?(^^;