implementation of readProcess for grep command line? - haskell

I have a shell command line which i want to use in Haskell ,f.e.:
grep -n "358" cameraTest.owl
I have already tried that on ghci :
readProcess "grep" ["-n","358"] "cameraTest.owl"
but it doesn't work.
How could i do with the function readProcess in Haskell ?
Getting error
Prelude System.Process> readProcess "grep" ["-n","358"] "cameraTest.owl"
*** Exception: readProcess: grep "-n" "358" (exit 1): failed

According to the documentation, the third parameter of readProcess is a string representing the standard input. But in your case, you seem to be grepping a file, so no standard input is needed. Just pass the file name as another of the arguments inside the list.
Like this:
readProcess "grep" ["-n","358","cameraTest.owl"] ""
But there's another problem with invoking grep, though. grep in Linux returns a non-zero exit code when no lines are found. But readProcess interprets any non-zero exit code as an error and throws an exception, which may not be the behaviour you want.
So one solution is to rely on the lower-level createProcess function, that doesn't throw an exception when the exit code is non-zero. But in that case you'll have to write code that reads the process' stdout by yourself.
Edit: as Tim mentions in his comment, you can also use readProcessWithExitCode. I forgot about that function.

Related

Prevent script running with same arguments twice

We are looking into building a logcheck script that will tail a given log file and email when the given arguments are found. I am having trouble accurately determining if another version of this script is running with at least one of the same arguments against the same file. Script can take the following:
logcheck -i <filename(s)> <searchCriterion> <optionalEmailAddresses>
I have tried to use ps aux with a series of grep, sed, and cut, but it always ends up being more code than the script itself and seldom works very efficiently. Is there an efficient way to tell if another version of this script is running with the same filename and search criteria? A few examples of input:
EX1 .\logcheck -i file1,file2,file3 "foo string 0123" email#address.com
EX2 .\logcheck -s file1 Hello,World,Foo
EX3 .\logcheck -i file3 foo email#address1.com,email#address2.com
In this case 3 should not run because 1 is already running with parameters file3 and foo.
There are many solutions for your problem, I would recommend creating a lock file, with the following format:
arg1Ex1 PID#(Ex1)
arg2Ex1 PID#(Ex1)
arg3Ex1 PID#(Ex1)
arg4Ex1 PID#(Ex1)
arg1Ex2 PID#(Ex2)
arg2Ex2 PID#(Ex2)
arg3Ex2 PID#(Ex2)
arg4Ex2 PID#(Ex2)
when your script starts:
It will search in the file for all the arguments it has received (awk command or grep)
If one of the arguments is present in the list, fetch the process PID (awk 'print $2' for example) to check if it is still running (ps) (double check for concurrency and in case of process ended abnormally previously garbage might remain inside the file)
If the PID is still there, the script will not run
Else append the arguments to the lock file with the current process PID and run the script.
At the end, of the execution you remove the lines that contains the arguments that have been used by the script, or remove all lines with its PID.

Why does my Haskell program not accept standard input redirection?

I'm trying to read a file in Haskell by supplying the file name as a command line argument.
I have read that you can accomplish this by:
./program < input.txt
I wrote this code:
main = do
[fileName] <- getArgs
file <- readFile fileName
print file
But I get this error: "pattern match failure in do expression". If I omit the < sign it works, is this the only way to accomplish this? I would much rather not omit it. What should I change?
./program < input.txt calls the program with 0 arguments and redirects stdin to the contents of input.txt.
So you get a pattern matching error because getArgs is empty. So if you want your program to always read from stdin, don't use the command line arguments at all and read from stdin instead of a file.
If you want your program to read from stdin only if no file name was given, check the length of the arguments first and then read from the given file name or from stdin depending on that.
If you run ./program arg then arg is passed as an argument. The standard input is left on its default -- usually reading from keyboard from a terminal.
If you run ./program < filename then no arguments are passed to the program. The standard input now is redirected so to read from file filename.
This is just how the OS shell works.
In Haskell, getArgs gets the program arguments. In the second case, they are empty, and [fileName] <- getArgs fails with your runtime error.

Bash does not print any error msg upon non-existing commands starting with dot

This is really just out of curiosity.
A typo made me notice that in Bash, the following:
$ .anything
does not print any error ("anything" not to be interpreted literally, it can really be anything, and no space after the dot).
I am curious about how this is interpreted in bash.
Note that echo $? after such command returns 127. This usually means "command not found". It does make sense in this case, however I find it odd that no error message is printed.
Why would $ anything actually print bash:anything: command not found... (assuming that no anything cmd is in the PATH), while $ .anything slips through silently?
System: Fedora Core 22
Bash version: GNU bash, version 4.3.39(1)-release (x86_64-redhat-linux-gnu)
EDIT:
Some comments below indicated the problem as non-reproducible at first.
The answer of #hek2mgl below summarises the many contributions to this issue, which was eventually found (by #n.m.) as reproducible in FC22 and submitted as a bug report in https://bugzilla.redhat.com/show_bug.cgi?id=1292531
bash supports a handler for situations when a command can't be found. You can define the following function:
function command_not_found_handle() {
command=$1
# do something
}
Using that function it is possible to suppress the error message. Search for that function in your bash startup files.
Another way to find that out is to unset the function. Like this:
$ unset -f command_not_found_handle
$ .anything # Should display the error message
After some research, #n.m. found out that the described behaviour is by intention. FC22 implements command_not_found_handle and calls the program /etc/libexec/pk-command-not-found. This program is part of the PackageKit project and will try to suggest installable packages if you type a command name that can't be found.
In it's main() function the program explicitly checks if the command name starts with a dot and silently returns in that case. This behaviour was introduced in this commit:
https://github.com/hughsie/PackageKit/commit/0e85001b
as a response to this bug report:
https://bugzilla.redhat.com/show_bug.cgi?id=1151185
IMHO this behaviour is questionable. At least other distros are not doing so. But now you know that the behaviour is 100% reproducible and you may follow up on that bug report.

multiline contents of a IO handle in haskell display nothing

I have been experimenting with Haskell. I am trying to write a web crawler and I need to use external curl binary (due to some proxy settings, curl needs to have some special arguments which seem to be impossible/hard to set inside the haskell code, so i rather just pass it as a command line option. but that is another story...)
In the code at the bottom, if I change the marked line with curl instead of curl --help the output renders properly and gives:
"curl: try 'curl --help' or 'curl --manual' for more information
"
otherwise the string is empty - as the `curl --help' response is multiline.
I suspect that in haskell the buffer is cleared with every new line. (same goes for other simple shell commands like ls versus ls -l etc.)
How do I fix it?
The code:
import System.Process
import System.IO
main = do
let sp = (proc "curl --help"[]){std_out=CreatePipe} -- *** THIS LINE ***
(_,Just out_h,_,_)<- createProcess sp
out <-hGetContents out_h
print out
proc takes as a first argument the name of the executable, not a shell command. That, is when you use proc "foo bar" you are not referring to a foo executable, but to an executable named exactly foo bar, with the space in its file name.
This is a useful feature in practice, because sometimes you do have spaces in there (e.g. on Windows you might have c:\Program Files\Foo\Foo.exe). Using a shell command you would have to escape spaces in your command string. Worse, a few other characters need to be escaped as well, and it's cumbersome to check what exactly those are. proc sidesteps the issue by not using the shell at all but passing the string as it is to the OS.
For the executable arguments, proc takes a separate argument list. E.g.
proc "c:\\Program Files\\Foo\\Foo.exe" ["hello world!%$"]
Note that the arguments need no escaping as well.
If you want to pass arguments to curl you have to pass that it in the list:
sp = (proc "/usr/bin/curl" ["--help"]) {std_out=CreatePipe}
Then you will get the complete output in the entire string.

How to check if command is available or existant?

I am developing a console application in C on linux.
Now an optional part of it (its not a requirement) is dependant on a command/binary being available.
If I check with system() I'm getting sh: command not found as unwanted output and it detects it as existent. So how would I check if the command is there?
Not a duplicate of Check if a program exists from a Bash script since I'm working with C, not BASH.
To answer your question about how to discover if the command exists with your code. You can try checking the return value.
int ret = system("ls --version > /dev/null 2>&1"); //The redirect to /dev/null ensures that your program does not produce the output of these commands.
if (ret == 0) {
//The executable was found.
}
You could also use popen, to read the output. Combining that with the whereis and type commands suggested in other answers -
char result[255];
FILE* fp = popen("whereis command", "r");
fgets(result, 255, fp);
//parse result to see the path of the bin if it has been found.
pclose(check);
Or using type:
FILE* fp = popen("type command" , "r");
The result of the type command is a bit harder to parse since it's output varies depending on what you are looking for (binary, alias, function, not found).
You can use stat(2) on Linux(or any POSIX OS) to check for a file's existence.
Use which, you can either check the value returned by system() (0 if found) or the output of the command (no output equal not found):
$ which which
/usr/bin/which
$ echo $?
0
$ which does_t_exist
$ echo $?
1
If you run a shell, the output from "type commandname" will tell you whether commandname is available, and if so, how it is provided (alias, function, path to binary). You can read the documentation for type here: http://ss64.com/bash/type.html
I would just go through the current PATH and see whether you can find it there. That’s what I did recently with an optional part of a program that needed agrep installed. Alternately, if you don’t trust the PATH but have your own list of paths to check instead, use that.
I doubt it’s something that you need to check with the shell for whether it’s a builtin.

Resources