ruby/python จัดการ command line arguments

มี server นึง (เขียนด้วย C/C++) ในบริษัทผมที่ผมต้องใช้งาน ต้องขอข้อมูลจากมันเป็นประจำ การที่จะเอา software เต็มๆ ไปขอข้อมูลจากมันบางทีก็เป็นเรื่องยุ่งยาก เขาก็เลยมีตัว test connection (เขียนด้วย C/C++ เช่นกัน) ไว้ทดสอบขอข้อมูลจาก server ตัวนั้นเพื่อดูว่ามันได้ข้อมูลไหม หรือเราลงถูกรึเปล่า ทีนี้ไอ้ตัว simple tool ตัวนี้มันใช้ยากพอดูเลย

$> ./runaction -s <server service> -h <server IP/Host> -e <encryption ไหม ตอบได้ yes/no> -p <server port> -f <file ใส่ชื่อ item ที่จะขอ>

จะเห็นว่าชื่อ parameter มันยาก แถมผมต้อง save ชื่อ item ที่จะขอลง file ถ้าจะขอหลาย item ก็ต้องเคาะบรรดทัดไปเรื่อยๆ แบบนี้

$> cat item.txt
facebook
oracle
ibm

แล้วก็ต้องใส่ item.txt ลงไปใน -f ข้างบน แล้ว server ก็จะส่งข้อมูลของ facebook, oracle และ ibm มาให้ แถมยังต้องใส่ env variable “ACTION_PATH” ไปที่ folder ของตัว simple tool นั้นอีก

ที่นี้ผมมีเรื่องที่จะต้องเปลี่ยน item บ่อยๆ เพื่อทดสอบอะไรบางอย่าง เลยอยากจะให้ใส่ชื่อ item ลงใน command เลยไม่ต้องไปสร้าง file เอง แถมทดสอบกับ server เดียวกันซ้ำๆ ก็ใส่ค่า default ไปเลย สรุป requirement ได้ดังนี้

  • ผมใส่ชื่อ item เป็น facebook,apple,vodaphone แล้วมันไปสร้าง file ชื่อ item ให้
  • มี default host, service, port, encryption ผมไม่จำเป็นต้องใส่ถ้าต่อกับ default server แต่ถ้าผมจะต่อกับ server ตัวอื่น ก็ต้องใส่ parameter อะไรไปในรูปแบบที่ง่ายกว่านี้
  • เฉพาะ item เป็น field ที่ต้องใส่เสมอ


จาก Requirement ข้างบน เลยเขียน script ง่อยๆ มาครอบ เริ่มที่ python ที่ผมถนัดโดยใช้ Argparse ที่เนยซดแนะนำ

#!/usr/local/bin/python

import os
import sys
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--host',help='input Host IP [optional]',type=str, default='127.0.0.1')
parser.add_argument('--service',help='input Host Service [optional]',type=str, default='ABC')
parser.add_argument('--port',help='input Host Port [optional]',type=str, default='8880')
parser.add_argument('--encrypt',help='input Host encryption [optional]',type=str,choices=['yes','no'],default='yes')
parser.add_argument('item',type=str)
args = parser.parse_args()

ric_file = './tmp_item.txt'
addN = lambda value: '%s\n'%(str(value))

with open(ric_file,'w') as foutput:
	for ric in args.item.split(','):
		foutput.write(addN(ric))

strCMD=""
os.putenv("ACTION_PATH","/home/ply/software/action")
strCMD='./runaction -s %s -h %s -e %s -p %s -f %s '%(args.service,args.host,args.encrypt,args.port,ric_file)

print strCMD
os.system(strCMD)

ข้อสังเกตุ

  • lambda addN นี่โผล่มาใน version แรกที่รองรับแค่ item เดียวเพราะถ้าไอ้ text file ไม่ตบบรรทัดใหม่ให้ tool แม่งเจ๊ง -*- ตอนนี้เลยไม่รู้ว่าต้องใส่รึเปล่า
  • จริงๆ ลองใช้ parser.add_argument(‘item’,nargs=”*”) แล้วจะได้ array [“item1″,”item2″,”item3”,…] เลยแต่มันรองรับแค่ space delimeter แบบ facebook ibm intel เท่านั้น ใช้ลูกน้ำไม่ได้ เลยย้อนกลับมาสั่ง split แม่ม

อีกภาษานึงที่ผมพยายามหัดมานานมากกกแล้วแต่ยังเขียนไม่เป็นสักทีคือ ruby ก็เลยลองเขียน code ข้างบนด้วย ruby กับ OptionParser logic ก็ลอกมาง่ายๆ เพราะมันตรงไปตรงมาอยู่แล้ว

#!/usr/local/bin/ruby
require 'optparse'

options={:host=>'127.0.0.1',:service=>'ABC',:port=>'8880',:encrpyt=>'yes',:file=>[]}

optparse = OptionParser.new do |opts|
	opts.banner = "Usage: simple.rb [options]"
	opts.separator ","
	opts.on("--host host","input Host IP [optional]") { |host| options[:host]=host }
	opts.on("--service service","input Host Service [optional]") { |service| options[:service]=service }
	opts.on("--port port","input Host Port [optional]") { |port| options[:port]=port }
	opts.on("--protocol OPT",[:yes,:no],"'input Host encrypt [optional]") do |encrpyt| 
		options[:encrpyt]=encrpyt
	end
	opts.on("-f a,b,c",Array,"Input Items name") { |f| options[:file]=f }
end.parse!

ric_file = './tmp_item.txt'
File.open(ric_file,'w') do |file|
	options[:file].each { |ric| file.puts ric }
end

ENV['ACTION_PATH']='/home/ply/software/action'
strCMD = "./runaction -s #{options[:service]} -h #{options[:host]} -e #{options[:encrpyt]} -p #{options[:port]} -f #{ric_file}"

exec(strCMD)

ข้อสังเกตุ

  • ส่วนตัวคิดว่า argparse ใน python ใช้ง่ายกว่า ruby optparse แต่ optparse ก็ทำให้ผมรับข้อมูลมาเป็น array ได้ตรงไปตรงมากว่า
  • optparse บน windows แม่งรับ array ไม่ได้ ไปลองบน linux แล้ว work -*-
  • วิธีที่ ruby จะรับ shell command มี 4 วิธี แต่ผมลองแล้ว work แค่ exec แฮะ
  • ยังหาวิธีทำ require argument ไม่ได้ -*- ต้องหาทางกันต่อไป

และแล้วชีวิตผมก็ง่ายขึ้นมาระดับนึงอ่ะนะ

One thought on “ruby/python จัดการ command line arguments

Leave a reply to वीर Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.