PureScriptで標準入力を扱う方法が案外分かりにくいのでサンプルコードを貼ってみます。
※ 以下のコードはPureScript 0.15.15で動作確認しています。
一行ずつ処理しなくて良い場合(かつMac/Linuxのみ)
大量のデータが入力されるユースケースでは標準入力を1行ずつ読み込んで都度処理しないとメモリを使い切ってしまう場合がありますが、そこまで大量でもない入力であれば以下のコードが一番シンプルです。
module Main where
import Prelude
import Effect (Effect)
import Effect.Console (log)
import Node.Encoding (Encoding(..))
import Node.FS.Sync (readTextFile)
main :: Effect Unit
main = do
cs <- readTextFile UTF8 "/dev/stdin"
log cs
ただし、/dev/stdinはWindowsでは使えないので、MacかLinuxで実行する場合しかこの方法は使えません。
プロジェクトの作成開始からの流れは以下のようになります。
spago init
spago install node-buffer node-fs
# src/Main.pursを編集
echo "hello" | spago run # テストを実行
一行ずつ処理しないといけない場合、もしくはWindowsにも対応したい場合
前述のプログラムと同様の処理をするコードは以下のようになります(node-readlineというパッケージを使うのがキモです)。
module Main where
import Prelude
import Effect (Effect)
import Effect.Console (log)
import Node.EventEmitter (on_)
import Node.Process (stdin)
import Node.ReadLine (createInterface, lineH)
main :: Effect Unit
main = do
interface <- createInterface stdin mempty
interface # on_ lineH \s ->
log s
プロジェクト作成開始からの流れは以下です。
spago init
spago install node-event-emitter node-process node-readline
# src/Main.pursを編集
echo "hello" | spago run # テストを実行
このコードの派生型として、その他のユースケースのサンプルコードも以下に載せておきます。
ユーザーとインタラクティブにやり取りしたい場合
module Main where
import Prelude
import Effect (Effect)
import Effect.Class.Console (log)
import Node.EventEmitter (on_)
import Node.ReadLine (close, createConsoleInterface, lineH, noCompletion, prompt, setPrompt)
main :: Effect Unit
main = do
interface <- createConsoleInterface noCompletion
setPrompt "> " interface
prompt interface
interface # on_ lineH \s ->
if s == "quit"
then close interface
else do log $ "Got " <> s
prompt interface
先頭一行だけ無視したい場合
module Main where
import Prelude
import Effect (Effect)
import Effect.Console (log)
import Node.EventEmitter (on_, once_)
import Node.Process (stdin)
import Node.ReadLine (createInterface, lineH)
main :: Effect Unit
main = do
interface <- createInterface stdin mempty
interface # once_ lineH \_ ->
interface # on_ lineH \s ->
log s
標準入力から1行ずつではなくすべて一括で処理したい場合
module Main where
import Prelude
import Data.Array as A
import Data.String as S
import Effect (Effect)
import Effect.Console (log)
import Effect.Ref as Ref
import Node.EventEmitter (on_)
import Node.Process (stdin)
import Node.ReadLine (closeH, createInterface, lineH)
main :: Effect Unit
main = do
readAll log
readAll :: (String -> Effect Unit) -> Effect Unit
readAll callbak = do
inputStrings <- Ref.new []
interface <- createInterface stdin mempty
interface # on_ lineH \s ->
Ref.modify_ (A.cons s) inputStrings
interface # on_ closeH do
cs <- (S.joinWith "\n" <<< A.reverse) <$> Ref.read inputStrings
callback cs
このコードでは、一行ずつ読み込んだテキストをinputStringsという参照に溜め込んでいき、標準入力から読み込むものがなくなったら、コールバックとして渡された関数にまとめて渡しています。
もっと良い方法があったら教えてください。他にも「こんなときどうすればよいか?」といったご質問があれば(分かる範囲で)回答したいと思いますので、お気軽にコメントください。
参考にしたソース:
https://github.com/purescript-node/purescript-node-readline/blob/master/test/Main.purs