How are command line argument are treated in toplevel? - ocaml

I have a program which takes command line argument. The same of source file is encode.ml. I want to load this file in the toplevel.
Is there way to load the source file in the toplevel where we can pass it a command line arguments?
Thanks.

Yes, invoke your toplevel with ocaml encode.ml arg1 arg2 etc. The following program demonstrates it:
$ cat args.ml
let () =
Array.iteri (Printf.printf "%d -> %s\n") Sys.argv
$ ocaml args.ml -h --help -help
0 -> args.ml
1 -> -h
2 -> --help
3 -> -help

Related

Global CLI flag in OCaml Core.Command

I am currently creating a CLI application in OCaml and using Core.Command, the CLI parser included in core (v0.10), to parse the command line.
I want to have a global flag that can be used for any subcommand (like the --paginate or --git-dir flags in git for example).
For instance, I want a -debug flag so that the two following commands are valid
my-cli -debug hello world
my-cli -debug goodbye world
However, I could not find a way to do this with the Core.Command API.
Here is a simplified version what I currently have.
open Core
let initialize_logger debug =
Logs.set_reporter (Logs_fmt.reporter ());
let log_level = if debug then Logs.Debug else Logs.Info in
Logs.set_level (Some log_level)
let some_func_with_logging () =
Logs.debug (fun m -> m "the flag debug was passed!")
let hello name =
some_func_with_logging ();
Printf.printf "Hello %s!\n" name
let goodbye name =
some_func_with_logging ();
Printf.printf "Goodbye %s!\n" name
let hello_command =
let open Command.Let_syntax in
Command.basic
~summary:"says hello"
[%map_open
let name = anon ("name" %: string)
and debug = flag "debug" no_arg ~doc:"debug" in
fun () ->
initialize_logger debug;
hello name
]
let goodbye_command =
let open Command.Let_syntax in
Command.basic
~summary:"says goodbye"
[%map_open
let name = anon ("name" %: string)
and debug = flag "debug" no_arg ~doc:"debug" in
fun () ->
initialize_logger debug;
goodbye name
]
let main_command =
Command.group ~summary:"a cool CLI tool"
[ ("hello", hello_command);
("goodbye", goodbye_command);
]
let () = Command.run main_command
There are two main issues here:
the debug flag as well as the call to initialize_logger is duplicated in every subcommand
the debug flag needs to be passed after the subcommand when invoking the command: my-cli hello world -debug instead of my-cli -debug hello world
Is there a clean way to handle this with Core.Command API?

Required argument for compiled program?

Is there any way to do required argument for crystal program?
For example
./myprog ~/Music -r
Instead of
./myprog -d ~/Music -r
So my program wont run if there's no [directory] argument. Right now using "option_parser" and can only do -arguments.
There is no way to create required arguments using option_parser, but you can parse arguments and throw an error or exit if there is no argument passed you expect:
require "option_parser"
directory = nil
parser = OptionParser.new
parser.on("-d DIR", "Directory [required]") do |d|
directory = d
end
parser.parse ARGV
if directory.nil?
# directory argument was not set
# print help and exit
puts parser
exit 1
else
# ...
end

Python process pipes with subprocess.Popen

Here is a test file:
gunzip -c file_1.gz
Line 1
Line 2
Line 3
I am executing bash commands this way:
cmd = "gunzip -c file_1.gz | grep 3"
subprocess.call(cmd, shell=True))
Line 3
I need to run this command on several files in parallel, then join the processes. SO it seems I have to use subprocess.Popen().communicate(). However Popen won't recognize the pipe correctly and will feed it to the first command, gunzip in my case:
subprocess.Popen(cmd.split()).communicate())
gunzip: can't stat: | (|.gz): No such file or directory
gunzip: can't stat: grep (grep.gz): No such file or directory
gunzip: can't stat: 8 (8.gz): No such file or directory
I would like to keep the whole command and to avoid separating it this way:
gunzip = subprocess.Popen('gunzip -c file_1.gz'.split(), stdout=subprocess.PIPE)
grep = subprocess.Popen('grep 3'.split(), stdin=gunzip.stdout, stdout=subprocess.PIPE)
gunzip.stdout.close()
output = grep.communicate()[0]
gunzip.wait()
Is there a way to not separate the commands and process the pipe correctly?
To run the grep 3 command you need the output from the previous command, so there is no way to run this successfully in a single command with subprocess.Popen.
If you always want to run grep 3 for all the files, you could just join the results of all the gunzip -c file_x.gz and then run the grep command only once on the entire list.
subprocess.Popen('gunzip -c file_1.gz'.split(), stdout=subprocess.PIPE)
subprocess.Popen('gunzip -c file_2.gz'.split(), stdout=subprocess.PIPE)
...
grep = subprocess.Popen('grep 3'.split(), stdin=all_gunzip_stdout, stdout=subprocess.PIPE)

Redirect standard output OCaml

How can you redirect the standard output in OCaml ?
I tried Format.set_formatter_out_channel but it doesn't seem to work. When I use printf afterwards, the text is still printed on the screen, and the file I created remains empty.
The reason your experiment failed is that Printf.printf doesn't use the output channel of the Format module. The Format module is for pretty-printing, a fairly elaborate task. The Printf.printf function writes formatted data to the standard output (a C-style printf).
Do you really want to redirect standard output, or do you just want to write to a specific channel? To write to a channel oc you can just use
Printf.fprintf oc ...
rather than
Printf.printf ...
Doing redirection is a different thing. You can do it with Unix.dup2. Here's an example session that shows how to do it:
$ cat redirected
cat: redirected: No such file or directory
$ cat redir.ml
let main () =
let newstdout = open_out "redirected" in
Unix.dup2 (Unix.descr_of_out_channel newstdout) Unix.stdout;
Printf.printf "line of text\n";
Printf.printf "second line of text\n"
let () = main ()
$ ocamlopt -o redir unix.cmxa redir.ml
$ ./redir
$ cat redirected
line of text
second line of text
Since this is changing low-level file descriptors behind the back of the OCaml I/O system, I'd be a little careful. As a quick hack it's fantastic--I've done it many times.
Update
Here's a version of the above code that redirects standard output temporarily, then puts it back where it was before.
$ cat redirected
cat: redirected: No such file or directory
$
$ cat redir.ml
let main () =
let oldstdout = Unix.dup Unix.stdout in
let newstdout = open_out "redirected" in
Unix.dup2 (Unix.descr_of_out_channel newstdout) Unix.stdout;
Printf.printf "line of text\n";
Printf.printf "second line of text\n";
flush stdout;
Unix.dup2 oldstdout Unix.stdout;
Printf.printf "third line of text\n";
Printf.printf "fourth line of text\n"
let () = main ()
$
$ ocamlopt -o redir unix.cmxa redir.ml
$ ./redir
third line of text
fourth line of text
$
$ cat redirected
line of text
second line of text

How to log all commands run By system() System Call

I am trying to debug a C++ application which invokes many command line applications such as grep, etc through a the system() system call. I need to see all the commands the application is executing through the system() call.
I tried to view these commands by enabling history and view the .history file. But these commands are not executed through a terminal. The history file has only the commands executed interactively.
Any idea how this can be done?
Define a new macro with similar name:
#define system(_x) std::cout << _x << std::endl; (system)(_x);
The system macro replaces the system function and:
It prints the command to the standard output (or elsewhere).
It calls the system function.
Thanks to Hasturkun's suggestion, the following is better:
#define system(_x) (std::cout << (_x) << std::endl, system(_x))
That returns the result of system function call, too ;-)
To trace every command executed by "yourProgram":
truss -s!all -daDf -t exec yourProgram
eg:
$ truss -s!all -daDf -t exec sh -c "/bin/echo hello world;/bin/date"
Base time stamp: 1282164973.7245 [ Wed Aug 18 22:56:13 CEST 2010 ]
5664: 0.0000 0.0000 execve("/usr/bin/i86/ksh93", 0x080471DC, 0x080471EC) argc = 3
5664: argv: sh -c /bin/echo hello world;/bin/date
5665: 0.0106 0.0106 execve("/bin/echo", 0x08067484, 0x080674F8) argc = 3
5665: argv: /bin/echo hello world
hello world
5664: 0.0126 0.0126 execve("/bin/date", 0x080674E0, 0x080674F8) argc = 1
5664: argv: /bin/date
Wed Aug 18 22:56:13 CEST 2010
If you want to correlate these execs to system() calls, you can use that command:
truss -t execve -f -u 'libc:system' yourProgram
eg:
$ cat a.c
main()
{
system("echo a b c");
system("pwd");
}
$ truss -t execve -f -u 'libc:system' ./a
20073: execve("a", 0x08047240, 0x08047248) argc = 1
20073/1#1: -> libc:system(0x8050a5c, 0x0)
20074/1: execve("/bin/sh", 0x080471BC, 0x08047248) argc = 3
a b c
20073/1#1: <- libc:system() = 0
20073/1#1: -> libc:system(0x8050a68, 0x0)
20076/1: execve("/bin/sh", 0x080471BC, 0x08047248) argc = 3
/tmp
20073/1#1: <- libc:system() = 0
Finally, if you are using Solaris 10 or newer, you can use Dtrace for this task like this:
dtrace -Z -q -c yourProgram -n ' pid$target:libc:system:entry { printf("system(\"%s\")\n", copyinstr(arg0)); } '
which will give that output with the same "a" code:
a b c
/tmp
system("echo a b c")
system("pwd")
PS: By the way system() isn't a system call but a standard library function.
You can use truss or strace (Not sure which one comes with Solaris) to run the program and trace the calls to system.
For truss the relevant command will be something like truss -caf program_name