roy > naoya > 基礎プログラミングII > (5)コマンドラインとの情報のやり取り

(5) 11/01の授業内容:コマンドラインとの情報のやり取り

[1]ARGV

ktermにおいてrubyプログラムを実行する際、

pan{c10xxxx}% ./prog.rb[Return]

とか

pan{c10xxxx}% ruby prog.rb[Return]

と入力した。

特殊な方法として、

pan{c10xxxx}% ruby prog.rb data.txt[Return]

のようにプログラム名の後ろにファイル名を指定すると、そのファイルの内容を読み込み、プログラムの中で取り扱うことができた。

ktermからプログラムを実行する際に、プログラム名の後ろに値を指定することで、その値をプログラム内で使用することができる。前期はファイル名のみを指定したが、次のように値を指定することもできる。

pan{c10xxxx}% ruby prog.rb 100 200 300[Return]

ktermからプログラムを実行する際に上記のように与えた値のことを引数(ひきすう)という。Rubyでは、プログラム実行時に与えた引数は自動的にARGVという配列変数に代入される。

上記の例では100、200、300という引数が与えられており、配列変数のARGVのインデックス0、1、2にそれぞれ
ARGV[0] = "100"
ARGV[1] = "200"
ARGV[2] = "300"
のように代入される。いずれも文字列であることに注意する必要がある。なお100 200 300の間はスペースのみでありカンマ等はないことに注意せよ

これを利用すると、プログラム実行時に与えた値に基づいて動作を決定することができる。

ARGVについてまとめ

ktermからプログラムを実行する際に引数を指定すると、ARGVという配列変数に代入される

  • 引数が1つの場合はARGV[0]に代入される
  • 引数が2つの場合は1つ目の値がARGV[0]、2つ目の値がARGV[1]に代入される
  • 引数が3つの場合は1つ目の値がARGV[0]、2つ目の値がARGV[1]、3つ目の値がARGV[2]に代入される
  • 引数が増えるごとにARGVのインデックスを1ずつ増やしながら順番に代入される
  • 引数に与えた値はARGVにすべて文字列として代入される
  • 引数として複数の値を与える場合は、引数の間はスペースとする

1からユーザが指定した任意の値までの足し算を行うプログラムを書いてみよう。これまでは、プログラム実行後に「好きな数字を入力してください」と表示し、キーボードから入力された値をgetsメソッドで取得するという以下のようなプログラムを書いてきた(sum-gets.rb)。

#!/usr/koeki/bin/ruby
sum = 0
print"好きな数字を入力してください"
goal = gets.chomp!.to_i

1.upto(goal) do |i|
  sum += i
end

printf("1から%dまでの合計は%dです\n",goal, sum)

これをARGVを使用して書き換えることを考えてみよう。仮に30までの合計を求める場合、ktermでプログラムを実行する際に以下のように引数を与える必要がある(プログラム名はsum-ARGV.rbとする)

pan{c10xxxx}% ruby sum-ARGV.rb 30[Return]

プログラムは以下の通りとなる。ktermのプログラムを実行する際に与えた引数である30はARGV[0]に代入される。文字列として取り扱われるため、goal = ARGV[0].to_iとし、整数に変換してからgoalに代入する。

#!/usr/koeki/bin/ruby

sum = 0
goal = ARGV[0].to_i

1.upto(goal) do |i|
  sum += i
end

printf("1から%dまでの合計は%dです\n",goal, sum)

[2]ファイル出力

pan{c10xxxx}% ruby prog.rb data.txt[Return]

とすると、data.txtを読み込んでprog.rbが処理を行ったが、

pan{c10xxxx}% ruby prog.rb > data.txt[Return]

とすると、prog.rbの実行結果がdata.txtに出力される。

hello.rb

#!/usr/koeki/bin/ruby
print"Hello! Ruby\n"

このプログラムを普通に実行すると、以下のようにktermにHello! Rubyと表示される。

pan{c10xxxx}% ruby hello.rb[Return]
Hello! Ruby

次に、出力先としてhello.outというファイルを指定してみよう。この場合は、実行をしてもktermには何も出力されない。ファイルの中味を表示するcatコマンドでhello.outを見てみると、ファイル内に結果が出力されていることがわかる。

pan{c10xxxx}% ruby hello.rb > hello.out[Return]
pan{c10xxxx}% 何も表示されない

pan{c10xxxx}% cat hello.out[Return]
Hello! Ruby ファイル内に出力されている

今回のプログラムはHello! Rubyと表示されるだけだが、出力先ファイルを指定すると、「テストの得点を入力して下さい」というようなユーザへのうながしのメッセージもファイルに出力されてしまう。

[3]STDERRとSTDIN

出力先や入力元が複数ある場合に、これらの制御を行うのが、前期にも使用したSTDERRSTDINであった。もう一度確認しておこう。

STDERR

STDERRを用いると出力先はディスプレイになる。以下のプログラムでは、STDERRをつけているものは常にディスプレイに出力される。STDERRをつけていないprintについては、出力先ファイルを指定していればファイルに、していなければディスプレイに出力される。

#!/usr/koeki/bin/ruby

STDERR.print"合言葉を入力せよ。\n"
word = gets.chomp!
if word == "ほげらー"
  STDERR.print"正解だ!パスワードをファイルに出力した\n"
  print"HXGUE\n"
else
  STDERR.print"はずれだ!\n"
end
pan{c10xxxx}% ruby word.rb[Return]
合言葉を入力せよ。
ほげらー[Return]
正解だ!パスワードをファイルに出力した
HXGUE パスワードがディスプレイに表示される
pan{c10xxxx}% ruby word.rb > word.out[Return]
合言葉を入力せよ。
ほげらー[Return]
正解だ!パスワードをファイルに出力した
パスワードはファイルに出力される

STDIN

ARGVを使用する場合、プログラム内でgetsメソッドを用いると予想外の挙動を示す。このプログラムは実行時に与えた引数がvar1に代入され、プログラム実行後にキーボードから入力した値がvar2に代入される。そしてvar1var2を足した結果を出力する(gets-ARGV.rb)。

#!/usr/koeki/bin/ruby

var1 = ARGV[0].to_i

print"好きな数字を入力してください\n"
var2 = gets.chomp!.to_i
answer = var1 + var2
printf("%d足す%dは%dです\n",var1, var2, answer)

このプログラムを実行すると以下のようにエラーになる。

pan{c10xxxx}% ruby gets-ARGV.rb 20[Return]
好きな数字を入力してください
gets-ARGV.rb:6:in `gets': No such file or directory - 20 (Errno::ENOENT)
        from gets-ARGV.rb:6

エラーメッセージを見ると「No such file or directory-20」すなわち「20というファイル、ディレクトリはありません」というメッセージである。

前期の授業で正規表現を扱ったときのことを思い出そう。外部に検索対象となるテキストファイル(例えばpiyo.txt)を保存しておき、そのファイル内の検索を行って正規表現にマッチする行のみを表示させるようなプログラムを書いてきたが、こうしたプログラムを実行する際には、下のように実行するプログラム名の後ろに読み込むファイル名を指定した。そしてプログラム内に書かれたgetsメソッドは実行時に指定したファイルから1行ずつ読み込むという働きを行った。

pan{c10xxxx}% ruby hoge.rb piyo.txt[Return]

gets-ARGV.rb内のgetsメソッドもこれと同じ働きをしている。プログラムを実行する際にプログラムの後ろに引数を与えているが、これを読み込むファイル名だと判断し、読み込もうとしたがファイルが見つからないというエラーが発生しているのである。このままでは都合が悪いので、var2 = STDIN.gets.chomp!.to_iとしよう。これでキーボードからの読み込みが可能となる。

STDINは標準入力という意味で、STDIN.getsでgetsメソッドの読み込み元を標準入力にせよという指定になる。標準的な入力はキーボードから行われるので、このような指定を行うことで、キーボードからの入力が受け付けられるようになる。

[4]プログラムの強制終了

sum-ARGV.rbでは、プログラム実行時に引数を与えることがプログラムを正常に実行する上での前提条件となっている。引数を与えなければ望むような実行結果は得られない。しかし、単に望んだ結果が得られないだけでは、ユーザは何がおかしいのか理解することができない。実行方法が間違っているのではなく、プログラム自体に誤りがあると考える可能性もある。この問題を解決するために、プログラムを改良してみよう。

#!/usr/koeki/bin/ruby
if ARGV[0] == nil
  STDERR.print"1からプログラム起動時に指定した値までの合計を出します\n"
  STDERR.print"50までの総和を計算する場合ruby goukei-ARGV.rb 50と入力\n"
  exit(1)
end

sum = 0
goal = ARGV[0].to_i

1.upto(goal) do |i|
  sum += i
end

printf("1から%dまでの合計は%dです\n",goal, sum)

このプログラムでは上の5行(黄色の部分)が追加されている。これは1つのif文である。ifの条件としてARGV[0] == nilを指定している。nilとはデータがないという意味であり、ARGV[0]にデータがないということは、プログラムを実行する際に引数を与えていないということを意味する。この場合にendの前にある3行を実行する。

  • STDERR.printの2行:メッセージをディスプレイに出力する。このメッセージを出すことでユーザはプログラムの実行方法が間違っていたことがわかる。このプログラムに関して言えば、STDERR.printではなくprintでもよい。STDERR.printは出力先としてディスプレイを指定する際の記法である。STDERR.printとすることでディスプレイへの表示を明示していると考えておけばよい。
  • exit(1):exitはプログラムの強制終了をあらわす。このプログラムでは引数を入力せずに実行した場合、プログラムを継続する意味がない。そこでexitを使用して強制的にプログラムを終了している。プログラムを終了させる場合、プログラムの実行がうまくいったかどうかをシステムに対して示すことができる。これを終了ステータスというが、プログラムがうまくいった場合は0を用い(exit(0))、うまくいかなかった場合は0以外とする(exit(1))決まりになっている。

[5]出席課題

本日は授業中に課題を実施する。授業の時間内でできたところまでメールで送る。

  • 課題:ARGVを用いて、素敵なプログラムを作成せよ。引数を入力せずに実行した場合は、適切な実行方法を説明する機能を組み込むこと。
  • 提出先:課題提出用メールアドレス
  • 提出期限:授業終了時
  • メールのSubject:attend05
  • 本文の構成:1行目で学籍番号、氏名を記載する。2行目以降は下記の構成とする
  1. 作成したプログラム
  2. プログラムの実行結果
  3. プログラムの説明
  4. 感想

  • 採点基準:出席点2点+出来栄えに応じて最大5点
  • わかりにくい説明や、Webページを単にコピー&ペーストしただけの説明は減点することがある。一度読み直してから提出すること。

Tips:emacsでの日本語入力のオンオフはCtrl+oです

Tips:ktermでのプログラムの実行結果をメールに貼り付けるには、コピーしたい箇所をマウスで選択し、emacs(Mew)上でマウスの真ん中ボタンをクリックする

Tips:Mewによるメールの送り方はMewコマンドを参照