Haskell read multiple files - haskell

I'm trying to read multiples files at once using the following code.
The code compiles, however when executed I get the following exception:
*** Exception: ..: openFile: permission denied (Permission denied).
I have all the permissions as system administrator. Also, I was able to do the same in C# with no problem, meaning/suspecting that it's an issue with the code itself.
Thanks for help.
main :: IO()
main = do
putStrLn " Enter file path:"
content <- getLine >>= getDirectoryContents
x <- mapM readFile content
print x

Are you sure all of the files are actually files? It looks like you're trying to open a file called ...
Filter the results to only include actual files first.

Related

Why doesn't my Haskell cmd line program get arguments from Vim Bang?

Vim has the possibility to let you replace selected text with the output of an external program. I'd like to take advantage of this with programs that I'd write in Haskell. But it doesn’t get the selected text as args.
-- show-input.hs
module Main where
import System.Environment
main = do
input <- getArgs
putStr ("Input was: " ++ (show input))
When I run it from the command line (NixOS GNU/Linux, BASH), I get the expected behavior:
$ ./show-input test
Input was: ["test"]
When I select some text in Vim and invoke :'<,'>!~/show-input, I get this :
Input was: []
There is something weird here, but I can't tell if it is from the way Vim passes arguments or from the way Haskell gets them. I have tried with both console Vim and graphical gVim (8.0.1451), with the same result.
NB: I can successfully use Vim Bang! with other external programs, such as grep. It works great.
---
Correct version after chepner's answer
So, for anyone interested, just replace getArgs with getContents and you get your input all in a string (instead of a list of strings).
module Main where
import System.Environment
main = do
input <- getContents
putStr ("Input was: " ++ (show input))
The ! command sends the seleted text to the program via standard input, not as a command line argument. The command line equivalent would be somecommand | ./show-input.

doesFileExist not finding files that exist

So I have a directory, /test, and in it are the following files:
test.txt
BasicLoop.vm
FibonacciSeries.vm
StackTest.vm
test.vm
I'm trying to read all the .vm files into one massive string, and in order to do so I thought to use filterM doesFileExist dir where dir is the result of getDirectoryContents, to remove directories and symlinks and whatever, but when I test it, the result of that filter is just BasicLoop.vm and test.vm. None of them are empty, and they show up when I peek at the contents of dir, so they clearly do exist. Why does the function say they don't?
Why does the function say they don't?
Because getDirectoryContents returns the file and directory names, not the full filepath. To see that, try something like:
λ:> getDirectoryContents "test"
["data","..","test.dats","."]

How to execute commands from Haskell as root?

Consider the following Haskell function:
eraseFile :: FilePath -> IO ()
eraseFile basename =
do let cmd' = ">"
args' = ("/path/to/file/" ++ basename) :: String
(exitcode', stdout', stderr') <- readProcessWithExitCode cmd' [args'] ""
return ()
When I try to run this in a stack ghci repl, or from the main function, I get a permission denied error from the console. Normally, in a bash console, you could just run this command as sudo, but this doesn't seem to work when invoked from Haskell.
Question: How to execute system commands in Haskell as root?
As already pointed out in the comments, you can just run the entire stack/ghc under root, but I daresay that's a bad idea. Preferrably, I'd just invoke sudo as a process from within your program. The particular command – emptying a file, if I have understood that correctly? – is then easiest done with tee:
do let cmd' = "sudo"
args' = ["tee", "/path/to/file/" ++ basename :: String]
(exitcode', stdout', stderr') <- readProcessWithExitCode cmd' args' ""
As Zeta remarks, truncate --size 0 would probably be a cleaner command.
To get around password entering, you probably also want to make an exception in the sudoers file. It's a hairy matter; of course the really best thing would be if you could avoid needing root permissions altogether.

Haskell compilation with an input file, error openFile: does not exist (No such file or directory)

I apologize if this is a simple question.
I have specified an input file which is in the same directory of the code source file.
isprime :: Int -> [Int] -> Bool
isprime ...
main = do
handle <- openFile "primes-to-100k.txt" ReadMode
contents <- hGetContents handle
i <- getLine
print $ isprime (read i::Int) $ map (\x->read x::Int) $ lines contents
hClose handle
The code runs well when I use "runhaskell Q111.hs" in shell,
but when I compile it with "ghc --make Q111.hs and run, I got an error message
Q111: primes-to-100k.txt: openFile: does not exist (No such file or directory)
logout
The point is that the code runs well with ghci and runhaskell, but the executable can't find the input txt.
Do I have to provide the input txt to compiler using someway?
How are you running the executable? The text file would have to be in the directory you run the program from, which may or may not be where the program is. If you are running ./Q111 from the command line, or if you are double-clicking Q111.exe in Windows, then the text file must be in the same folder as the executable. If you are in a different directory on the command line, then the text file would have to be wherever your current directory is, and not the directory where the executable is.
EDIT: Just saw from your other comment that you are on OS X, and (I assume) are double-clicking the program. From what you said I guess that OS X sets the current directory of such-executed programs to be your home. If you want to get the directory of your program, see the answers to this question. If you use the FindBin package they mention:
main = do
exedir <- getProgPath
handle <- openFile (exedir ++ "/primes-to-100k.txt") ReadMode
...
Try the curdDir <- getCurrentDirectory and paths <- getDirectoryContents and check the paths. If it contains the filename, the path is right(and try using contents <-readFile "file" for simpler programs, but this isn't neccessary).
Edit:
...and use isexist <- doesFileExist "f" for checking the existence.

Prompt message disappears in compiled executable?

I have a Haskell program that shows a prompt and then accepts input from the command line. I'm doing this as:
main = do putStr "Please enter program source file name: "
programFileName <- getLine
programFileHandle <- openFile programFileName ReadMode
program <- hGetContents programFileHandle
putStr "Please enter initial file configuration file name: "
initConfigFileName <- getLine
initConfigFileHandle <- openFile initConfigFileName ReadMode
initConfigStr <- hGetContents initConfigFileHandle
print (evaluateProgram (lines program) (readReg initConfigStr))
When I run it on the GHCi interpreter, the prompts show up fine and I am able to enter my inputs (and everything else works).
e.g.
*Main> main
Please enter program source file name: sum.urm
Please enter initial file configuration file name: sum.conf
9
When I compile it though (on Mac OS X or Windows), it produces an executable that does not show my prompts. It waits for the two input strings, and then once I have put in the valid filenames, it prints the prompts and the result.
e.g.
$ ./a.out
sum.urm
sum.conf
Please enter program source file name: Please enter initial file configuration file name: 9
Any ideas why this is happening?
For the curious, I was implementing an Unlimited Register Machine in Haskell.
The standard output stream, stdout, is line buffered by default. That means that it will only be written to the console every time you output a \n character, or finally when the program terminates. You can fix this by importing System.IO and doing hFlush stdout after every putStr that doesn't contain a \n at the end.
This is a buffering issue. Here are related questions, with several choices for solutions:
Why isn't my IO executed in order?
Execution order with (>>=) not what I expected

Resources