I have a main function that does a lot of IO. At one point, however, I want to check a variable like not (null shouldBeNull) exit the whole program, without continuing, with a linux exitcode 1 and output an error message.
I've tried playing around with error "..." like putting that in an if:
if (not (null shouldBeNull)) error "something bad happened" else putStrLn "ok"
but I get a parse error (possibly incorrect indentation) :(.
Here's an altered snippet.
main :: IO ExitCode
main = do
--Get the file name using program argument
args <- getArgs
file <- readFile (args !! 0)
putStrLn("\n")
-- ... (some other io)
-- [DO A CHECK HERE], exit according to check..
-- ... (even more io)
echotry <- system "echo success"
rmtry <- system "rm -f test.txt"
system "echo done."
As you may notice, I want to do the check where I've put [DO A CHECK HERE] comment above...
Thanks for your help!
The parse error is because you're missing the then keyword in your if expression.
if condition then truePart else falsePart
For exiting, a more appropriate choice than error might be to use one of the functions from System.Exit, for example exitFailure.
So for example,
if not $ null shouldBeNull
then do putStrLn "something bad happened"
exitFailure
else putStrLn "ok"
Related
import System.Process
createProcess (shell "pwd") -- /Users/username/current-directory
createProcess (shell "cd app") -- LOST
createProcess (shell "pwd") -- /Users/username/current-directory
Obviously, createProcess (shell "cd app") is not persistent in the next process.
But, how can I keep the session persistent?
I know I can pass cwd but
createProcess (shell "mkdir some-dir && cd some-dir")
createProcess (shell "pwd") { cwd = Just "some-dir" }
But, I have to parse the previous command to get "some-dir."
Is there something better than parsing the command?
First a working code example:
module Main where
import System.Process
import System.IO
ourShell :: CreateProcess
ourShell = (proc "/bin/bash" []) { std_in = CreatePipe, std_out = CreatePipe }
main = do
(Just bashIn, Just bashOut, Nothing, bashHandle) <- createProcess ourShell
hSetBuffering bashIn NoBuffering
hSetBuffering bashOut NoBuffering
print "Sending pwd"
hPutStrLn bashIn "pwd"
print "reading response"
hGetLine bashOut >>= print
print "Sending cd test"
hPutStrLn bashIn "cd test"
print "reading response"
-- hGetLine bashOut >>= print you need to know there is no answer ....
print "Sending pwd"
hPutStrLn bashIn "pwd"
print "reading response"
hGetLine bashOut >>= print
hPutStrLn bashIn "exit"
hGetContents bashOut >>= print
ec <- waitForProcess bashHandle
print ec
This outputs on my machine in /tmp with an existing /tmp/test:
"Sending pwd"
"reading response"
"/tmp"
"Sending cd test"
"reading response"
"Sending pwd"
"reading response"
"/tmp/test"
""
ExitSuccess
You start a shell and connect a pipe to its input stream and a pipe to its output stream. Now you can send commands to its input stream and read the responses from its output stream via the connected pipes.
But now you need a protocol, so you know what output belongs to which command. So you need to know, for example, how many output lines will be generated for which output. If you for exampled tried to read a response for the cd test command, your program would hang, as there isn't any output.
There are other ways to handle that, but they all involve heuristics of some kind and exceed the scope of the question.
You cannot use an external program to change the current directory of the current program. That just won't work.
It is for this reason that cd is a shell built-in operator, not an external program. (At least, that's how Unix does it. I'm not 100% sure about Windows.)
Try using setCurrentDirectory instead. That should allow you to change your Haskell program's current directory, which will then stay permanent for the rest of the program's run (or until you change it again).
This question already has an answer here:
Wrong IO actions order using putStr and getLine
(1 answer)
Closed 5 years ago.
When I want to put some text before reading an input in Haskell, I tried writing it like this:
putStr "enter value: "
var <- getLine
However the output requires the user input before it displays the text:
[input]
enter value:
When I use putStrLn instead of putStr, it displays as it should:
enter value:
[input]
Why do these two statements function differently? Is it the addition of the newline?
putStr "enter value: " actually writes to an output buffer, which is flushed to the actual standard output only later on, when the buffer becomes full or when a newline is found.
This is roughly the same mechanism found in the C programming language.
So, even if putStr "enter value: " is run before getLine, we don't see the output message, yet, which feels wrong.
The solution is to flush the standard output handle explicitly.
import System.IO
-- ...
putStr "enter value: "
hFlush stdout
var <- getLine
I am writing a cgi-script in Haskell.
I am restricted to use only hugs/runhugs.
#!/opt/local/bin/runhugs
module Main where
main = do
putStrLn ("content-type: text/plain\n")
putStrLn ("Hello, Server!")
So far so good.
But now I want to get the server's environment variables.
For example the "SCRIPT_NAME" environment variable.
With bash I can do:
#!/bin/bash
echo "content-type: text/plain;"
echo ""
echo $SCRIPT_NAME
With the result: /path/to/script.cgi in the browser-window.
For Haskell I found something alike: script <- getEnv "SCRIPT_NAME",
but
#!/opt/local/bin/runhugs
module Main where
main = do
putStrLn ("content-type: text/plain\n")
scriptname <- getEnv "SCRIPT_NAME"
putStrLn scriptname
doesn't work.
Is it possible to do it in a way sort of like that ?
plain without an import, or
with an import possible in hugs
Try import System.Environment (getEnv).
The following snippet of code executes a grep command and binds the output to stdout', stderr' and errCode respectively.
main :: IO ()
main = do
let stdin' = ""
(errCode, stdout', stderr') <- readProcessWithExitCode "grep" ["search-term" ,"-nr", "/path/to/be/searched"] stdin'
putStrLn $ "stdout: " ++ stdout'
putStrLn $ "stderr: " ++ stderr'
putStrLn $ "errCode: " ++ show errCode
The problem is that stdout' only captures the first result of the search. I think this is because grep is something akin to a spawned process that feeds search results one file by one file until it is finished.
Question: I need the entire output of the standard output of grep to be binded to stdout'. Is this possible in Haskell, and if so, what is an idiomatic way to do it?
EDIT: It turns out that the issue wasn't as I thought it was. Namely, I had only one file in my directory that wasn't symlinked. I forgot to use -R as an option with grep, and so those symlinks weren't being traversed in the search! The issue has been resolved.
I'm trying to get the GET and the POST from the Happstack tutorial into one handler function so it's always together, which I've kind of achieved, but it seems ugly.
login :: ServerPart Response
login = msum [
do methodM POST
user <- look "user"
pass <- look "pass"
success <- query $ CheckPassword user pass
ok $ toResponse (user ++ ", " ++ pass ++ ": " ++ (if success then "Valid" else "Invalid")),
ok $ toResponse $ html $ do
B.head $ do
title "Login Form"
B.body $ do
form ! enctype "multipart/form-data" ! B.method "POST" $ do
B.label "user: " >> input ! type_ "text" ! name "user" ! size "10"
B.label "pass: " >> input ! type_ "text" ! name "pass" ! size "10"
input ! type_ "submit" ! name "upload"]
Things I'd like to change are:
Explicitly call out methodM GET rather than just have it be the fallthough.
Pull out the redundant ok $ toResponse and have that only in one place.
Preferably have the POST return HTML as well.
Anything else that looks "off" to anyone with more experience. Any ideas?
UPDATE: figured out #1; adding do methodM GET above the ok $ toResponse $ ... works fine, but the thing for newbies like me to note is that must line up vertically, i.e., the m in methodM needs to be directly above the o in ok. Hopefully this saves someone hours of frustration.
UPDATE 2: #3 was fairly easy -- just update the last line of the POST to be ok $ toResponse $ html $ do B.body $ toHtml $ user ++ ...
Look up formlets (they work with Happstack) and/or digestive-functors (which work with Snap and maybe even Happstack):
http://hackage.haskell.org/package/formlets
http://hackage.haskell.org/package/digestive-functors
I haven't investigated how digestive-functors are better than formlets but it's newer package and might be simpler than the old one.
There are some examples
There's even a library in F# that compiles to JavaScript and does similar thing on client side. It allowes checking things like login availability from JS while still being written in nice formlet/functional style. It's called WebSharper:
WebSharper