let rec to_shell: type a. _ -> a t -> string =   fun params e ->     let continue e = to_shell params e in     let seq =       function       | [] -> ":"       | l -> String.concat  ~sep:params.statement_separator l in     let die s =       match params.die_command with       | Some f -> f s       | None ->         ksprintf failwith           "Die command not set: you cannot use the `fail` construct together with the `~no_trap:true` option (error message was: %S)" s     in     let expand_octal s =       sprintf         {sh| printf -- "$(printf -- '%%s' %s | sed -e 's/\(.\{3\}\)/\\\1/g')" |sh}         s in     let to_argument varprefix =       let argument ?declaration ?variable_name argument =         object           method declaration = declaration           method export = Option.map ~f:(sprintf "export %s ; ") declaration           method variable_name = variable_name           method argument = argument         end in       function       | `String (Literal (Literal.String s)) when Literal.String.easy_to_escape s ->         argument (Filename.quote s)       | `String (Literal (Literal.String s)) when           Literal.String.impossible_to_escape_for_variable s ->         ksprintf failwith "to_shell: sorry literal %S is impossible to escape as `exec` argument" s       | `String v ->         let variable_name = Unique_name.variable varprefix in         let declaration =           sprintf "%s=$(%s; printf 'x')"             variable_name (continue v |> expand_octal)         in         argument ~variable_name ~declaration           (sprintf "\"${%s%%?}\"" variable_name)       | `Int (Literal (Literal.Int s)) -> argument (Int.to_string s)       | `Int other ->         let variable_name = Unique_name.variable varprefix in         let declaration = sprintf "%s=%s" variable_name (continue other) in         argument ~variable_name ~declaration           (sprintf "\"${%s%%?}\"" variable_name)      in     match e with     | Exec l ->       let variables = ref [] in       let args =         List.mapi l ~f:(fun index v ->             let varname = sprintf "argument_%d" index in             let arg = to_argument varname (`String v) in             match arg#declaration with             | None -> arg#argument             | Some vardef ->               variables := sprintf "%s ; " vardef :: !variables;               arg#argument) in       (List.rev !variables) @ args       |> String.concat ~sep:" "       |> sprintf " { %s ; } "     | Raw_cmd s -> s      | Returns {expr; value} ->       sprintf " { %s ; [ $? -eq %d ] ; }" (continue expr) value     | Bool_operator (a, op, b) ->       sprintf "{ %s %s %s ; }"         (continue a)         (match op with `And -> "&&" | `Or -> "||")         (continue b)     | String_operator (a, op, b) ->       sprintf "[ \"%s\" %s \"%s\" ]"         (continue a)         (match op with `Eq -> "=" | `Neq -> "!=")         (continue b)     | No_op -> ":"     | If (c, t, e) ->       seq [         sprintf "if { %s ; }" (continue c);         sprintf "then %s" (continue t);         sprintf "else %s" (continue e);         "fi";       ]     | While {condition; body} ->       seq [         sprintf "while { %s ; }" (continue condition);         sprintf "do %s" (continue body);         "done"       ]     | Seq l -> seq (List.map l ~f:continue)     | Not t ->       sprintf "! { %s ; }" (continue t)     | Redirect_output (unit_t, redirections) ->       (*           We're here compiling the redirections into `exec` statements which          set up global redirections; we limit their scope with `( .. )`.          E.g.          (  exec 3>/tmp/output-of-ls ; exec 2>&3 ; exec 1>&2 ; ls ; ) ;        *)       let make_redirection { take; redirect_to } =         let takearg = to_argument "redirection_take" (`Int take) in         let retoarg =           to_argument "redirection_to"             (match redirect_to with `Fd i -> `Int i | `Path p -> `String p) in         let variables =           takearg#export :: retoarg#export :: [] |> List.filter_opt in         let exec =           sprintf "\"exec %%s>%s%%s\" %s %s"             (match redirect_to with `Fd _ -> "&" | `Path _ -> "")             takearg#argument             retoarg#argument         in         sprintf "%s eval \"$(printf -- %s)\" || { echo 'Exec %s failed' >&2 ; } "           (String.concat variables ~sep:"")           exec           exec       in       begin match redirections with       | [] -> continue unit_t       | one :: more ->         continue (Seq (             Raw_cmd (sprintf "( %s" (make_redirection one))             ::             List.map more ~f:(fun r -> Raw_cmd (make_redirection r))             @ [unit_t]             @ [ Raw_cmd ")" ]           ))       end     | Write_output { expr; stdout; stderr; return_value } ->       let ret_arg =         Option.map return_value ~f:(fun v -> to_argument "retval" (`String v))       in       let var =         Option.((ret_arg >>= fun ra -> ra#export) |> value ~default:""in       let with_potential_return =         sprintf "%s { %s %s ; }" var (continue expr)           (Option.value_map ret_arg ~default:"" ~f:(fun r ->                sprintf "; printf -- \"$?\" > %s" r#argument))       in       let redirections =         let make fd =           Option.map             ~f:(fun p -> {take = Construct.int fd; redirect_to = `Path p}) in         [make 1 stdout; make 2 stderr] |> List.filter_opt in       continue (Redirect_output (Raw_cmd with_potential_return, redirections))     | Literal lit ->       Literal.to_shell lit     | Output_as_string e ->       sprintf "\"$( { %s ; } | od -t o1 -An -v | tr -d ' \\n' )\"" (continue e)     | Int_to_string i ->       continue (Output_as_string (Raw_cmd (sprintf "printf -- '%%d' %s" (continue i))))     | String_to_int s ->       let var =  Unique_name.variable "string_to_int" in       let value = sprintf "\"$%s\"" var in       (* We put the result of the string expression in a variable to          evaluate it once; then we test that the result is an integer          (i.e. ["test ... -eq ...."] parses it as an integer). *)       sprintf " $( %s=$( %s ) ; if [ %s -eq %s ] ; then printf -- %s ; else %s ; fi ; ) "         var         (continue s |> expand_octal)         value value value         (die (sprintf "String_to_int: error, $%s is not an integer" var))     | Bool_to_string b ->       continue (Output_as_string (Raw_cmd (sprintf "printf -- '%s'"                                              (continue b))))     | String_to_bool s ->       continue (         If (           (String_operator (s, `EqLiteral (Literal.String "true"))),           (Raw_cmd "true"),           If (             (String_operator (s, `EqLiteral (Literal.String "false"))),             (Raw_cmd "false"),             (Fail))         )       )     | List l ->       (* Lists are newline-separated internal represetations,          prefixed by `G`. *)       let output o = sprintf "printf -- 'G%%s' \"%s\"" (continue o) in       let outputs = List.map l ~f:output in       let rec build =         function         | [] -> []         | [one] -> [one]         | one :: two :: t ->           one :: "printf -- '\\n'" :: build (two :: t)       in       (seq (build outputs))     | List_to_string (l, f) ->       continue (Output_as_string (Raw_cmd (continue l)))     | String_to_list (s, f) ->       continue s |> expand_octal |> sprintf "printf -- '%%s' \"$(%s)\""     | String_concat sl ->       let outputing_list = continue sl in       sprintf "$( { %s ; } | tr -d 'G\\n' )" outputing_list     | List_append (la, lb) ->       seq (continue la :: "printf -- '\\n'" :: continue lb :: [])     | List_iter (l, f) ->       let variter = Unique_name.variable "list_iter_var" in       let varlist = Unique_name.variable "list_iter_list" in       let outputing_list = continue l in       seq [         sprintf "export %s=\"$(%s)\" " varlist outputing_list;         sprintf "for %s in ${%s} " variter varlist;         "do : "(* we cannot put a `;` after do so the first command is no-op *)         continue (f (fun () ->             (* Here we remove the `G` from the internal represetation: *)             Raw_cmd (sprintf "${%s#G}" variter)));         "done";       ]     | Int_bin_op (ia, op, ib) ->       sprintf "$(( %s %s %s ))"         (continue ia)         begin match op with         | `Div -> "/"         | `Minus -> "-"         | `Mult -> "*"         | `Plus -> "+"         | `Mod -> "%"         end         (continue ib)     | Int_bin_comparison (ia, op, ib) ->       sprintf "[ %s %s %s ]"         (continue ia)         begin match op with         | `Eq -> "-eq"         | `Ge -> "-ge"         | `Gt -> "-gt"         | `Le -> "-le"         | `Lt -> "-lt"         | `Ne -> "-ne"         end         (continue ib)     | Feed (string, e) ->       sprintf {sh|  %s | %s  |sh}         (continue string |> expand_octal) (continue e)     | Pipe [] -> ":"     | Pipe l ->       sprintf " %s "         (List.map l ~f:continue |> String.concat ~sep:" | ")     | Getenv s ->       let var = Unique_name.variable "getenv" in       let value = sprintf "\"$%s\"" var in       let cmd_outputs_value =         (* We need to get the output of the `string t` and then do a `$<thing>`            on it:            f () { printf "HOME" ;}            aa=$(printf "\${%s}" $(f)) ; eval "printf \"$aa\""            And the `` | tr -d '\\n' `` part is because `\n` in the variable name            just “cuts” it, it wouldn't fail and `${HOME\nBOUH}` would be            equal to `${HOME}`         *)         sprintf "{ %s=$(printf \\\"\\${%%s}\\\" $(%s | tr -d '\\n')) ; eval \"printf -- '%%s' %s\" ; } "           var (continue s |> expand_octal) value in       continue (Output_as_string (Raw_cmd cmd_outputs_value))     | Setenv (variable, value) ->       sprintf "export $(%s)=\"$(%s)\""         (continue variable |> expand_octal)         (continue value |> expand_octal)     | With_signal {signal_name; catch; run} ->       let var = Unique_name.variable "with_signal" in       let value = sprintf "\"$%s\"" var in       continue Construct.(seq [           Raw_cmd (sprintf "export %s=$$" var);           exec ["trap"; continue catch; signal_name];           exec [             (* We need the `sh -c ...` in order to properly create a subprocess,                if not we break when `With_signal` are enclosed, the kill                command does not wake up the right process. *)             "sh""-c";             run (Raw_cmd (sprintf " kill -s %s %s ; kill $$ " signal_name value))             |> continue           ];         ])     | Fail -> die "EDSL.fail called"