Go to the first, previous, next, last section, table of contents.


The Shell module

Calling commands and pipelines

The following functions are simplified versions of the Shell_sys.job abstraction.

exception Subprocess_error of (string * Unix.process_status) list;;

The string contains the called commands in a readable representation. The list enumerates the return codes of the processes that have been started for the commands.

type producer
type consumer
type assignment

val command :
      ?cmdname:string ->                   (*> default: derived from filename *)
      ?arguments:(string array) ->         (*> default: empty *)
      ?environment:Shell_sys.environment ->  (* default: current environment *)
      ?descriptors:(Unix.file_descr list) ->

default: stdin, stdout, stderr

      ?assignments:(assignment list) ->

default: empty

:string ->
        Shell_sys.command

Creates a command descriptor, to be used in 'call'. ~name: The name of the command to invoke. If the name contains a '/', it is simply interpreted as the filename of the executable. Otherwise the command is searched in the current PATH. ~arguments: The arguments of the command (_without_ argv0). For the other options of the function, see Shell_sys.command (which can be used alternately).

val cmd :
      ?cmdname:string ->                   (* default: derived from filename *)
      ?environment:Shell_sys.environment ->  (* default: current environment *)
      ?descriptors:(Unix.file_descr list) ->

default: stdin, stdout, stderr

      ?assignments:(assignment list) ->

default: empty

:string ->
      args:string list ->
        Shell_sys.command

The same as 'command' but with a slightly different interface: cmd "ls" "/dir/file" instead of command ~arguments:|"/dir/file"| "ls"

val call :
      ?ignore_error_code:bool ->              (* default: false *)
      ?mode:Shell_sys.group_mode ->           (* default: Same_as_caller *)
      ?stdin:producer ->
      ?stdout:consumer ->
      ?stderr:consumer ->
      Shell_sys.command list ->
	unit

Starts the pipeline represented by the list of commands; i.e. if c1;c2;...;cN is passed, this corresponds to the pipeline c1 | c2 | ... | cN (in shell notation). If ~stdin is present, the first process of the pipeline reads input from the passed producer. If ~stdout is present, the last process of the pipeline writes output to the passed consumer. If ~stderr is present, all processes of the pipeline write their error messages to the passed consumer.

The function returns normally if all processes can be started and terminate regularly with exit code 0. If a process terminates with some other exit code, and ~ignore_error_code is set, the function returns normally, too. The latter does not apply if a process terminates because of a signal. If a process terminates with an exit code other than 0 and ~ignore_error_code is not set (the default), or if a process is terminated because of a signal, the exception Subprocess_error is raised. For every command the process result is passed back. If a process cannot be started (e.g. because of insufficient resources), the function tries to shut down the already running part of the pipeline by sending SIGTERM to these processes. It is not checked whether the processes actually terminate (no "wait" invocation for them); an appropriate exception is raised. In the case that it is not even possible to perform these cleanup actions, the exception Shell_sys.Fatal_error will be raised.

When the function raises an exception other than Subprocess_error, a serious error condition has happened, and it is recommended to exit the program as soon as possible.

val assign : src:Unix.file_descr -> target:Unix.file_descr -> assignment

val ( >& ) : Unix.file_descr -> Unix.file_descr -> assignment

val ( <& ) : Unix.file_descr -> Unix.file_descr -> assignment

assign src target: src >& target: src <& target: Arranges a redirection such that writing to src or reading from src will actually write to target or read from target. (In reality, the target descriptor is duplicated and replaces the src descriptor just before the process is launched.)

Note: >& and <& (and assign) are all the same function. The operators >& and <& are notations coming from the Bourne shell.

Examples: stdout >& stderr: If the process writes to stdout the data will be redirected to stderr. stdin <& f: Where f is an open file If the process reads from stdin the data will find the way from descriptor f

val stdin  : Unix.file_descr

val stdout : Unix.file_descr

val stderr : Unix.file_descr

The standard descriptors; defined here for convenience.

val from_string :
      ?pos:int ->                  (* default: 0 *)
      ?len:int ->                  (* default: until end of string *)
      ?epipe:(unit -> unit) ->     (* default: empty function *)
      string ->
	producer

Returns a function which can be used as ~stdin argument and which takes its material from a string (or, if ~pos or ~len are present, from the specified substring). If the pipeline crashes, the function ~epipe is called, and the descriptor is closed.

val from_stream :
      ?epipe:(unit -> unit) ->     (* default: empty function *)
      string Stream.t ->
        producer

Returns a function which can be used as ~stdin argument and which takes its material from a stream of strings. If the pipeline crashes, the function ~epipe is called, and the descriptor is closed.

val from_file : string -> producer

Arranges that the first process of the pipeline reads stdin from the passed file.

val from_fd : Unix.file_descr -> producer

Arranges that the first process of the pipeline reads stdin from the passed file descriptor.

val from_dev_null : producer

Arranges that the first process of the pipeline reads stdin from /dev/null

val to_buffer :
      Buffer.t ->
        consumer

Returns function which can be used as ~stdout or ~stderr argument. The consumed material is redirected to the buffer.

val to_file : ?append:bool -> string -> consumer

The consumer material is written to the file. If ~append, the material is appended; otherwise the file is truncated and overwritten.

val to_fd : Unix.file_descr -> consumer

The consumed material is redirected to the descriptor

val to_dev_null : consumer

The consumed material is redirected to /dev/null

Examples

Objective Caml version 3.00

--- SIMPLE COMMAND INVOCATION ---

# open Shell;; # call command "ls" ;; IDEAS s1.ml~ shell.mli~ shell_sys.ml~ unix_exts.ml META shell.a shell.ml~ shell_sys.o unix_exts.mli Makefile shell.cma shell_sys.cmi t unix_exts.mli~ Makefile~ shell.cmi shell_sys.cmo testjob unix_exts.ml~ depend shell.cmo shell_sys.cmx testjob~ unix_exts.o libshell.a shell.cmxa shell_sys.ml unix_exts.cmi unix_exts_c.c log shell.ml shell_sys.mli unix_exts.cmo unix_exts_c.c~ s1.ml shell.mli shell_sys.mli~ unix_exts.cmx unix_exts_c.o - : unit = ()

--- "BACKTICKS": REDIRECTING STDOUT TO A BUFFER ---

# let b = Buffer.create 10;; val b : Buffer.t = <abstr> # call ~stdout:(to_buffer b) command "ls" ;; - : unit = () # Buffer.contents b;; - : string = "IDEAS\nMETA\nMakefile\nMakefile~\ndepend\n..." and so on

--- SUBPROCESS ERRORS ---

# call command ~arguments:[| "/a" |] "ls" ;; /bin/ls: /a: No such file or directory Uncaught exception: Shell.Subprocess_error "/bin/ls", Unix.WEXITED 1.

--- REDIRECTING STDERR TO A BUFFER ---

# Buffer.clear b;; - : unit = () # call ~stderr:(to_buffer b) command ~arguments:[| "/a" |] "ls" ;; Uncaught exception: Shell.Subprocess_error "/bin/ls", Unix.WEXITED 1. # Buffer.contents b;; - : string = "/bin/ls: /a: No such file or directory\n"

--- PIPELINES ---

# call command ~arguments:[|"META"|] "cat"; command "sort" ;; archive(byte) = "shell.cma" archive(native) = "shell.cmxa" description = "Unix shell functions" linkopts = "-cclib -lshell" requires = "unix str" version = "0.0" - : unit = ()

--- PIPELINES + REDIRECTION ---

# Buffer.clear b;; - : unit = () # call ~stdout:(to_buffer b) command ~arguments:[|"META"|] "cat"; command "sort" ;; - : unit = () # Buffer.contents b;; - : string = "archive(byte) = \"shell.cma\"\narchive(native) = \"shell.cmxa\"\ndescription = \"Unix shell functions\"\nlinkopts = \"-cclib -lshell\"\nrequires = \"unix str\"\nversion = \"0.0\"\n"

--- REDIRECTION FROM A STRING ---

# let s = "f\na\nd\nc\n";; val s : string = "f\na\nd\nc\n" # call ~stdin:(from_string s) command "sort" ;; a c d f - : unit = ()

--- BOTH REDIRECTIONS ---

# Buffer.clear b;; - : unit = () # call ~stdout:(to_buffer b) ~stdin:(from_string s) command "sort" ;; - : unit = () # Buffer.contents b;; - : string = "a\nc\nd\nf\n"

--- REDIRECTION + ASSIGNMENT ---

# Buffer.clear b;; - : unit = () # call ~stdout:(to_buffer b) command ~assignments:[ stderr >& stdout ] ~arguments:[| "/a" |] "ls" ;; Uncaught exception: Shell.Subprocess_error "/bin/ls", Unix.WEXITED 1. # Buffer.contents b;; - : string = "/bin/ls: /a: No such file or directory\n"

Of course, all features can be combined arbitrarily. - Note that error reporting is better than in a traditional shell, because the exit codes of all started commands are returned. (Shells usually only return the exit code of the last command of a pipeline.) - For non-standard pipelines, you can also use the functions in Shell_sys. "call" is a simple concatenation of Shell_sys invocations.

---------------------------------------------------------------------- Below are some thoughts about functions that might be useful for system programming. They are not yet realized. ----------------------------------------------------------------------

type call_arg = Path_arg of string (* %p *) | String_arg of string (* %s *) | List_arg of string list (* %l *) | Descriptor of Unix.file_descr (* %d *) | Open_file of Unix.file_descr (* %f *)

val callf : ?ignore_error_code:bool -> (* default: false *) ?mode:Shell_sys.group_mode -> (* default: Same_as_caller *) ?environment:Shell_sys.environment -> (* default: current env *) ?path:(string list) -> (* default: use PATH *) ?stdin:producer -> ?stdout:consumer -> ?stderr:consumer -> string -> (* pipeline in shell notation *) call_arg list -> unit (* This is the simplified version of "call": The pipeline is passed in shell notation, and may contain placeholders in the style of printf (the reason why this function is called callf). Simple example: callf "cat file.txt | sort" creates a pipeline with two members, "cat" and "sort", and passes the argument "file.txt" to "cat". Example with placeholders: callf "cat %l | sort" List_arg [ "file1.txt"; "file2.txt" ] Here, the arguments for "cat" are not constant but a variable list of strings. For every placeholder %p, %s, %l, %d or %f there must be exactly one corresponding call_arg, and the type of the placeholder must be compatible with the variant of call_arg (see type declaration above).

%p, %s: These are simple strings which may occur as stand-alone words or embedded within words (e.g. %s.txt). %p is only compatible with Path_arg; %s only with String_arg. A (Path_arg p) is first searched in the current search path, and the expanded file name replaces %p. A (String_arg s) exactly substitutes the corresponding %s.

%l: This stands for a list of strings; this placeholder must only occur as command argument. For every value of the list passed by List_arg the word containing %l is instantiated; e.g. %l.txt with List_arg "a";"b";"c" will expand to "a.txt", "b.txt", "c.txt". If a word contains %l, it must not contain another placeholder.

%d: Refers to a descriptor of the subprocess, to be used in descriptor assignments. For example: callf "myscript %d>&%d" Descriptor stderr; Descriptor stdout A %d must correspond to a Descriptor value.

%f: Refers to a descriptor of the current process (i.e. an open file), to be used in descriptor assignments. For example: callf "myscript %d>%f" Descriptor stderr; Open_file f - where f is a file open for writing. A %f must correspond to an Open_file value.

The following notations are recognized:

First, the string is separated into words which are delimited by spaces, htabs, or pipe symbols.

The list of words is now separated into commands, separated by pipe symbols.

Words containing "<" or ">" count as descriptor assignments. The remaining words are analyzed as follows: The first word is the command name. The other words are the arguments of the command.

You can include spaces, htabs, |, %, < and > symbols as part of a word by preceding them with percent symbol (e.g. %| is the character '|' and not the command separator '|'). Caution: Besides % there is no other quoting mechanism; neither single nor double quotes nor backslashes can be used to indicate word boundaries.

Unlike the shell, the command is not again splitted into words after the placeholders have been replaced by their corresponding values.

The following descriptor assignments are possible: - n>&m where n,m numbers or %d: The descriptor n becomes a duplicate of m (regardless of whether m is open for reading or writing) - n>name where n is a number or %d, and name is a file name (may contain %s) or name is %f: The descriptor n of the subprocess writes to the file - n>>name where n is a number or %d, and name is a file name (may contain %s) or name is %f: The descriptor n of the subprocess appends to the file - n<name where n is a number or %d, and name is a file name (may contain %s) or name is %f: The descriptor n of the subprocess reads from the file - n<>name where n is a number or %d, and name is a file name (may contain %s) or name is %f: The descriptor n of the subprocess is opened for reading and writing to the file Note that the forms n>%f, n>>%f, n<%f, n<>%f are equivalent; it is recommended to choose the notation which reminds the reader of the intended purpose of the assignment.

Optional arguments: - See also "call" above. - ~environment: The environment to be passed to the processes. - ~path: The search path used for command searching. Commands (both constant commands and commands passed by Path_arg) are searched in the path only if they do not contain a slash character '/'. If ~path is not present, the environment variable PATH is scanned for the search path. To reject commands not containing a slash: ~path: To switch off command searching: ~path:"." )

val list_files : ?name_pattern:string -> (* default: every name is included *) ?filter:(string -> bool) -> (* default: fun _ -> true *) ?recursive:bool -> (* default: false *) ?follow_symlinks:bool -> (* default: false *) ?directory:bool -> (* default: false *) ?sorted:bool -> (* default: true *) ?omit_dot:bool -> (* default: true *) ?omit_dot_dot:bool -> (* default: true *) ?omit_hidden:bool -> (* default: true *) string -> string list (* List the files of the passed directory (yes, it _must_ be a directory).

~name_pattern: Include only files whose names match the regular expression (Str-like expression). Only the name of the files count, not the path before the last '/' ~filter: Include only files for which the filter returns 'true'. ~recursive: If the listed files contain directories other than "." and "..", these are recursively listed, too. Unless, ~follow_symlinks is set, symbolic links are not followed in this case. ~follow_symlinks: If set, symbolic links are resolved when descending into the file tree. Note that a symlink on the toplevel is always followed (even if ~directory is set). ~directory: If set, the passed directory itself is prepended to the output (e.g.: list_files ~directory:false "." = "file1"; "file2" , but list_files ~directory:true "." = "."; "./file1"; "./file2" ) ~sorted: Every directory list is sorted before output. ~omit_dot: The file "." is not output (unless it is the name of the passed directory) ~omit_dot_dot: The file ".." is not output (unless it is the name of the passed directory) ~omit_hidden: Files beginning with a dot are not output (unless it is the name of the passed directory) )

val iter_files : ?pattern:string -> ?filter:(string -> bool) -> ?recursive:bool -> ?follow_symlinks:bool -> ?directory:bool -> ?sorted:bool -> ?omit_dot:bool -> ?omit_dot_dot:bool -> ?omit_hidden:bool -> f:(string -> unit) -> string -> unit (* For every file of the output set, the function ~f is invoked. For the other arguments, see list_files. )

(* TODO: preorder/postorder sorting *)

(* User-friendly file tests: *)

val exists : string -> bool val is_regular : string -> bool val is_not_empty : string -> bool val is_directory : string -> bool val is_symlink : string -> bool val is_named_pipe : string -> bool val is_socket : string -> bool val is_special : string -> bool val is_block_special : string -> bool val is_character_special : string -> bool val is_suid : string -> bool val is_sgid : string -> bool val is_readable : ?effectively:bool -> string -> bool val is_writable : ?effectively:bool -> string -> bool val is_executable : ?effectively:bool -> string -> bool val is_newer_than : string -> string -> bool val are_the_same : string -> string -> bool

(* User-friendly file operations: *)

val rm : ?force:bool -> (* default: false *) ?only_symlink:bool -> (* default: false *) ?recursively:bool -> string -> unit (* ~force: do not fail if the file does not exists or permission do not suffice ~only_symlink: only remove the file if it is a symlink; otherwise fail (unless ~force) )

type lnmode = New | New_in_directory | New_or_directory | Update | Update_in_directory | Update_or_directory

(* TODO: ln = modes New, Update ln_into = modes New_in_directory, Update_in_directory )

(* New: newname must be a non-existing name in an existing directory New_in_directory: if newname is an existing directory, create a new link for the file there New_or_directory: one of the cases New, New_in_directory Update: if newname is non-existing: see New. If newname exists, it must not be a directory, and the link is updated Update_in_directory: newname must be an existing directory. If the link already exists in this directory, update it; otherwise create it Update_or_directory: one of the cases Update, Update_in_directory )

val ln : ?mode:lnmode -> (* default: New_or_directory *) oldname:string -> newname:string -> unit (* creates or updates a hard link *)

val symln : ?mode:lnmode -> (* default: New_or_directory *) oldname:string -> newname:string -> unit (* creates or updates a symbolic link *)

val cp : ?recursively:bool -> (* default: false *) ?parents:bool -> (* default: false *) ?follow_symlinks:bool -> (* default: false *) ?force:bool -> (* default: false *) ?unlink_src:bool -> (* default: false *) ?install:bool -> (* default: false *) ?perms:int -> (* default: derived from umask *) ?user:string -> (* default: real user *) ?group:string -> (* default: real group *) ?preserve_timestamp:bool -> (* default: false *) ?preserve_perms:bool -> ?preserve_user:bool -> ?preserve_group:bool -> ?create_missing_dirs:bool -> (* default: false *) src:string -> dest:string -> unit (* This "cp" will fail when copying special files *) (* ~parents: see cp --parents (questionable) ~install: removes dest before making the copy

~preserve_xxx beats ~xxx for files that existed as source. However, for newly created directories the ~xxx options count.

)

val cp_into : ?recursively:bool -> (* default: false *) ?follow_symlinks:bool -> (* default: false *) ?unlink_src:bool -> (* default: false *) src:string list -> dest:string -> (* must be a directory *) unit

val mv : ?force:bool -> (* default: false *) ?only_symlink:bool -> (* default: false *) src:string -> dest:string -> unit

val mv_into : ?force:bool -> (* default: false *) ?only_symlink:bool -> (* default: false *) src:string list -> dest:string -> unit

val mkdir (* esp. mkdir -p *) val rmdir val chmod (* mit symbolischer Angabe *) val chown (* mit ausgeschriebenen Usern *) val touch val file_size val file_user val file_group val file_atime val file_ctime val file_mtime val du val cat val md5sum

(* Module: Shell_tar: access to the "tar" command Shell_cpio Shell_text: line-by-line text processing ) ----------------------------------------------------------------------


Go to the first, previous, next, last section, table of contents.