# Module Shell_fs

    module Shell_fs: sig .. end

Shell filesystem

This module emulates a filesystem by submitting shell commands. For example, a directory listing is retrieved via the `ls` utility instead of opening the directory directly. This also works when logging in to a remote machine, e.g. via `ssh`.

The following standard POSIX commands are used:

- `dd` with options `if`, `of`, `bs`, `skip`, `conv=notrunc`, and optionally `excl` (the latter is a GNU extension)
- `test` with options `-d`, `-e`, `-f`, `-r`, `-s`, `-w`, `-x`
- `ls` with options `-1`, `-n`, `-d`, `-a`, `-L`
- `rm` with options `-r` and `-f`
- `mv` with option `-f`
- `ln` with option `-s`
- `mkdir` with options `-p`
- `rmdir`
- `cp` with option `-p`

In addition to this, the commands may be embedded in one-line shell scripts.

Filenames with leading minus chars are not supported.

Error reporting is less accurate than for a local filesystem.

Example. List a directory on a remote system:

          let fs =
            Shell_fs.shell_fs
              (Shell_fs.ssh_interpreter ~host:"moon" ())
          let files =
            fs # readdir [] "/this/is/a/directory/on/moon"

`type ``command_context = {`

`` | `sfs_command : `string`;` | `(*` | The command line | `*)`
`` | `sfs_stdin : `Shell.producer`;` | `(*` | stdin from here | `*)`
`` | `sfs_stdout : `Shell.consumer`;` | `(*` | stdout goes here | `*)`
`` | `sfs_stderr : `Shell.consumer`;` | `(*` | stderr goes here | `*)`
`` | `mutable sfs_status : `Unix.process_status option`;` | `(*` | The exit code is put here | `*)`

}

    type command_interpreter

The interpreter runs the command, and fills in `sfs_status`

    val local_interpreter : unit -> command_interpreter

Executes commands on the local machine

    val cmd_interpreter : (command_context -> Shell_sys.command list) ->
           command_interpreter

Creates a command interpreter from a function that creates the real command (as pipeline) to execute

    val ssh_interpreter : ?options:string list ->
           ?user:string -> host:string -> unit -> command_interpreter

Executes commands via ssh on the machine `host` as `user` (defaults to current user). `options` are further command-line options. By default, only `-o BatchMode yes` is passed.

    class type shell_stream_fs = object .. end

    class shell_fs : ?encoding:Netconversion.encoding -> ?root:string -> ?dd_has_excl:bool -> ?tmp_directory:string -> ?tmp_prefix:string -> command_interpreter -> shell_stream_fs

    val shell_fs : ?encoding:Netconversion.encoding ->
           ?root:string ->
           ?dd_has_excl:bool ->
           ?tmp_directory:string ->
           ?tmp_prefix:string ->
           command_interpreter -> shell_stream_fs

The shell filesystem.

- `encoding`: the assumed character encoding of the filenames. `None` by default.
- `root`: the root of the file tree that is accessed. This can be an absolute path, or a relative path.
- `dd_has_excl`: whether the `dd` command support "conv=excl". Default is `false`; this is a GNU extension.
- `tmp_directory` and `tmp_prefix` are only meaningful for the `write_file` method which creates a temporary file. See `Netchannels.make_temporary_file` for more information.

## Utility functions for developing other implementations of `shell_stream_fs`

    val execute : command_interpreter -> command_context -> unit

Starts this command. It is not waited until the command is finished. One can either call `wait` for this, or one of the adapter functions below.

One can only start one command at a time.

    val wait : command_interpreter -> unit

Waits until the running command is finished

    val output_stream_adapter : ci:command_interpreter ->
           close_in:(unit -> unit) ->
           skip:int64 -> Shell.consumer * Netchannels.in_obj_channel

Arranges that the output of a shell command is made available as an `in_obj_channel`:

     let (c, ch) = output_stream_adapter ~ci ~close_in ~skip

The consumer `p` can be used in a `command_context` for either `sfs_stdout` or `sfs_stderr`. The channel `ch` is an input channel, and when reading from it will return the bytes of stdout or stderr.

`close_in` is called as post-hook when the `close_in` method of `ch` is called.

`skip` bytes of stdout/stderr are skipped at the beginning of the stream.

    val input_stream_adapter : ci:command_interpreter ->
           close_out:(unit -> unit) -> Shell.producer * Netchannels.out_obj_channel

Arranges that the input of a shell command is made available as an `out_obj_channel`:

     let (p, ch) = input_stream_adapter ~ci ~close_in

The producer `p` can be used in a `command_context` for `sfs_stdin`. The channel `ch` is an output channel, and bytes written to it will appear in stdin of the executed command.

`close_out` is called as post-hook when the `close_out` method of `ch` is called.