さわっても熱くない花火

ちょっとした驚きを食べながら生きています

Rubyでシェルのコマンドを実行して、さらに標準入力に任意の値を食わせる

Rubyを使って、シェルのコマンドを実行する必要があったのですが、ちょっと手間取ったので、それに関するメモ

普通にコマンドを実行

Rubyさんは便利なもんで、言語の規格としてコマンド実行の手段が用意されています。

`mkdir hogehoge`

このように書くとコマンドが実行できます。便利ですね。
ちなみにこの文の戻り値がコマンドの実行結果となっています。

puts `ls -la`


しかし、この手法にはちょっと問題があります。
コマンドを実行した後に標準入力を食わせる必要のあるコマンドを実行したいときに、標準入力を食わせることができません。あら残念
ということで、そのようなことをする必要がある際は、次の方法をとる必要があります


コマンドを実行後、標準入力を食わせる方法

「リダイレクションを使って、コマンドの入出力をストリームで開く」って感じなのかな?

result=open("| ftp ftp://user:pass@hogehoge","r+")

result.puts("ls")
result.puts("help")
result.puts("exit")

while l=result.gets
  puts l
end

例だと、hogehogeにftp接続をして、lsとhelpを実行して結果を取得しています。
実行結果例はこんな感じ

% ruby kakisute.rb
drw-rw-rw-   1 user     group           0 Jun 20 08:55 FTPHome
Commands may be abbreviated.  Commands are:

!		features	mls		prompt		site
$		fget		mlsd		proxy		size
account		form		mlst		put		sndbuf
append		ftp		mode		pwd		status
ascii		gate		modtime		quit		struct
bell		get		more		quote		sunique
binary		glob		mput		rate		system
bye		hash		mreget		rcvbuf		tenex
case		help		msend		recv		throttle
cd		idle		newer		reget		trace
cdup		image		nlist		remopts		type
chmod		lcd		nmap		rename		umask
close		less		ntrans		reset		unset
cr		lpage		open		restart		usage
debug		lpwd		page		rhelp		user
delete		ls		passive		rmdir		verbose
dir		macdef		pdir		rstatus		xferbuf
disconnect	mdelete		pls		runique		?
edit		mdir		pmlsd		send
epsv4		mget		preserve	sendport
exit		mkdir		progress	set

しかしながら、これも色々と欠点があります。
同じストリームに対して、コマンドの出力を見ながら、コマンドを実行するなどということができません。
なので、得られた標準出力の結果から、何が実行されたかを類推する必要があります
result.putsの位置をgetsの後に持っていってもうまくいきません。何とも面倒な話です。


もしかしたら、リダイレクションで標準入力をファイルにつなげて、そのファイルをRubyから開いて書き込む。という方法をすればうまくいくかもしれません。試してないけど。

TetraMAXの-shellに特定の処理を食わせる

私がしたかったのはこれ。
まぁこんなことをしたい人が世の中にどれくらい居るかは知りませんが・・・
書き捨てコードだけど一応メモっておきます。

#! ruby -Ku
class TMaxShellRun
    def initialize(obj=[])
        @commandList=obj
    end

    #コマンドをAddする
    def add(value)
        @commandList.push(value)
    end

    def run
        i=0
        strs=[]
        result=open("| tmax -shell","r+")

        for com in @commandList
            result.puts com
        end

        tempStr=""
        tempL=[]

        while l=result.gets

            if tempL.size!=0 && l.include?('-T>')
                tempL.push(tempStr)
                strs.push(tempL.clone)
                tempL=[@commandList[i]]
                l.match(/.+-T> *(.*)$/)
                tempStr=" " + $1 + "\n"
                i+=1
            elsif tempL.size!=0
                tempStr+=l
            elsif l.include?('-T>')
                tempL=[@commandList[i]]
                tempStr=l.sub(/.+-T>(.*)$/,"")
                i+=1
            end
        end
        result.close

        @returnlist=strs
    end
    attr_accessor :returnlist
end

#ここからTMaxShellRunクラスの使い方
obj = TMaxShellRun.new
obj.add('read_netlist class.v -library')
obj.add('read_netlist netlist.v')
obj.add('run_build_model netlist')
obj.add('run_drc')
obj.add('add_faults -all')
obj.add('report_faults -all -collapsed')
obj.add('exit')
obj.run

puts "#{obj.returnlist[-1][0]}の実行結果は"
puts obj.returnlist[-1][1]

例では、対象回路の代表故障を出力させています。
Rubyの書き方の流儀がよくわからないからなんかアレ*1

*1:どう考えてもこのコード汚い