yunomuのブログ

酒とゲームと上から目線

HaskellでOSコマンド実行

当たり前の事を当たり前にやりたいよね。

ちょっとしたスクリプトを書く時に、OSのコマンドを実行してその結果をgrepしてcutしてとか、そういう事をよくやる。Haskellでやるかどうかっていうのは別として、そういうのってどうやるんだろうと思って。
grepとかcutとかはHaskell的にはパーサを書くのかもしれないけど、まずはコマンド実行から。

とりあえずコマンド実行

マニュアルを"cmd"とかで検索するとSystem.Cmdとかが出る。
System.Cmd
使い方はこう。

Prelude> import System.cmd
Prelude System.Cmd> system "pwd"
/home/user
ExitSuccess

ExitSuccessが戻り値で、画面出力はなんかそのまま標準出力に出ちゃう。

引数があっても

> system "netstat -l"

と指定できる。すごいこれパースしてんの? 中を見るとshell関数を呼んでて、さらに中のほうで"/bin/sh -c str"という感じで実行してた。なるほど。
rawSystemの方は、引数を文字列のリストとして渡すパターン。

標準出力を文字列として受け取る

まあsystemでもghciでためしに実行結果を見たいとかいう時は便利なんでしょうけど、文字列が取りたいよね。
って時はreadProcessでいける。
readProcess :: System.Process

readProcess :: FilePath -> [String] -> String -> IO String

プログラムのパスと引数リストと標準入力を取って文字列を返すと。標準入力が文字列なところが、なんか「へぇ〜」って感じですよね。
とりあえずこれでやろうと思えばできる。

import System.Process

main :: IO ()
main = do
    s <- readProcess "ifconfig" [] []
    putStrLn s

readFileとかと同じ感じですね。

標準出力のハンドルを受け取る

文字列じゃなくてハンドルをもらってなんか色々やりたい。
という時はcreateProcessにオプション渡してっていうかcreateProcessに渡すCreateProcessに"stdout = CreatePipe"を指定してあげるといいらしい。

import System.Process
import GHC.IO.Handle

getStdOut :: String -> IO Handle
getStdOut cmd = do
    (_, Just out, _, _) <-
      createProcess (shell cmd){ std_out = CreatePipe }
    return out

main :: IO ()
main = do
    h <- getStdOut "ifconfig"
    s <- hGetContents h
    putStrLn s

これでさっきのと同じ動きをします。
あとはhGetLineを使うなりなんなり。hIsEOFとかもあるからいいんじゃないかな。