Shells in OCaml
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

Non-posix &> redirection

It seems many a script uses the shorthand &> to redirect stdout and
stderr at the same time (usually to /dev/null).

+110 -42
+12 -2
src/lib/ast.ml
··· 476 476 | IoFile_LessGreat_FileName a -> 477 477 let a = filename a.value in 478 478 (Io_op_lessgreat, a) 479 + | IoFile_AndGreat_FileName a -> 480 + let a = filename a.value in 481 + (Io_op_andgreat, a) 479 482 | IoFile_Clobber_FileName a -> 480 483 let a = filename a.value in 481 484 (Io_op_clobber, a) ··· 780 783 | _, Some Ampersand -> true 781 784 | _ -> super#complete_command v ctx 782 785 783 - method! separator_list f l ctx = 786 + method! clist l ctx = 784 787 let v = 785 788 Nlist.to_list l 786 789 |> List.exists (function _, Ampersand -> true | _ -> false) 787 790 in 788 - if v then v else super#separator_list f l ctx 791 + if v then v else super#clist l ctx 792 + 793 + method! term t ctx = 794 + let v = 795 + Nlist.to_list t 796 + |> List.exists (function _, Ampersand -> true | _ -> false) 797 + in 798 + if v then v else super#term t ctx 789 799 end 790 800 in 791 801 o#complete_command ast false
+23
src/lib/eunix.ml
··· 25 25 let background () = 26 26 let _pgrid = Unix.getpid () in 27 27 () 28 + 29 + let fd_of_int (fd : int) : Unix.file_descr = Obj.magic fd 30 + 31 + let with_redirections (rdrs : Types.redirect list) fn = 32 + let saved_stdin = Unix.dup Unix.stdin in 33 + let saved_stdout = Unix.dup Unix.stdout in 34 + let saved_stderr = Unix.dup Unix.stderr in 35 + List.iter 36 + (function 37 + | Types.Redirect (i, fd, _) -> 38 + Eio_unix.Fd.use_exn "with_redirections" fd @@ fun fd -> 39 + Unix.dup2 fd (fd_of_int i) 40 + | Types.Close fd -> Eio_unix.Fd.close fd) 41 + rdrs; 42 + Fun.protect 43 + ~finally:(fun () -> 44 + Unix.dup2 saved_stdin (fd_of_int 0); 45 + Unix.dup2 saved_stdout (fd_of_int 1); 46 + Unix.dup2 saved_stderr (fd_of_int 2); 47 + Unix.close saved_stdin; 48 + Unix.close saved_stdout; 49 + Unix.close saved_stderr) 50 + fn
+49 -35
src/lib/eval.ml
··· 108 108 Eio.Path.open_in ~sw (ctx.fs / Ast.word_components_to_string file) 109 109 in 110 110 let fd = Eio_unix.Resource.fd_opt r |> Option.get in 111 - Some (Types.Redirect (n, fd, `Blocking)) 111 + [ Types.Redirect (n, fd, `Blocking) ] 112 112 | Io_op_lessand -> ( 113 113 match file with 114 114 | [ WordLiteral "-" ] -> 115 - if n = 0 then Some (Types.Close Eio_unix.Fd.stdin) 115 + if n = 0 then [ Types.Close Eio_unix.Fd.stdin ] 116 116 else 117 117 let fd = fd_of_int ~sw n in 118 - Some (Types.Close fd) 118 + [ Types.Close fd ] 119 119 | [ WordLiteral m ] when Option.is_some (int_of_string_opt m) -> 120 120 let m = int_of_string m in 121 - Some 122 - (Types.Redirect 123 - (n, fd_of_int ~close_unix:false ~sw m, `Blocking)) 124 - | _ -> None) 121 + [ 122 + Types.Redirect 123 + (n, fd_of_int ~close_unix:false ~sw m, `Blocking); 124 + ] 125 + | _ -> []) 125 126 | (Io_op_great | Io_op_dgreat) as v -> 126 127 (* Simple file creation *) 127 128 let append = v = Io_op_dgreat in ··· 130 131 (ctx.fs / Ast.word_components_to_string file) 131 132 in 132 133 let fd = Eio_unix.Resource.fd_opt w |> Option.get in 133 - Some (Types.Redirect (n, fd, `Blocking)) 134 + [ Types.Redirect (n, fd, `Blocking) ] 134 135 | Io_op_greatand -> ( 135 136 match file with 136 137 | [ WordLiteral "-" ] -> 137 - if n = 0 then Some (Types.Close Eio_unix.Fd.stdout) 138 + if n = 0 then [ Types.Close Eio_unix.Fd.stdout ] 138 139 else 139 140 let fd = fd_of_int ~sw n in 140 - Some (Types.Close fd) 141 + [ Types.Close fd ] 141 142 | [ WordLiteral m ] when Option.is_some (int_of_string_opt m) -> 142 143 let m = int_of_string m in 143 - Some 144 - (Types.Redirect 145 - (n, fd_of_int ~close_unix:false ~sw m, `Blocking)) 146 - | _ -> None) 144 + [ 145 + Types.Redirect 146 + (n, fd_of_int ~close_unix:false ~sw m, `Blocking); 147 + ] 148 + | _ -> []) 149 + | Io_op_andgreat -> 150 + (* Yesh, not very POSIX *) 151 + (* Simple file creation *) 152 + let w = 153 + Eio.Path.open_out ~sw ~create:(`If_missing 0o644) 154 + (ctx.fs / Ast.word_components_to_string file) 155 + in 156 + let fd = Eio_unix.Resource.fd_opt w |> Option.get in 157 + [ 158 + Types.Redirect (1, fd, `Blocking); 159 + Types.Redirect (2, fd, `Blocking); 160 + ] 147 161 | Io_op_clobber -> Fmt.failwith ">| not supported yet." 148 162 | Io_op_lessgreat -> Fmt.failwith "<> not support yet.") 149 163 | Ast.IoRedirect_IoHere _ -> ··· 267 281 | `Global p -> (true, p) 268 282 | `Local p -> (false, p) 269 283 in 284 + let rdrs = 285 + List.fold_left 286 + (fun acc -> function 287 + | Ast.Suffix_word _ -> acc 288 + | Ast.Suffix_redirect rdr -> 289 + handle_one_redirection ~sw:pipeline_switch ctx rdr @ acc) 290 + [] suffix 291 + |> List.rev 292 + in 270 293 match Built_ins.of_args (executable :: args_as_strings) with 271 294 | Some (Ok bi) -> 272 - let ctx = handle_built_in ~stdout:some_write ctx bi in 295 + let ctx = handle_built_in ~rdrs ~stdout:some_write ctx bi in 273 296 close_stdout ~is_global some_write; 274 297 let built_in = ctx >|= fun _ -> () in 275 298 let job = handle_job ~pgid job (`Built_in built_in) in ··· 301 324 in 302 325 loop saved_ctx job (pgid, some_read) rest 303 326 | None -> ( 304 - let redirect = 305 - List.fold_left 306 - (fun acc -> function 307 - | Ast.Suffix_word _ -> acc 308 - | Ast.Suffix_redirect rdr -> 309 - handle_one_redirection ~sw:pipeline_switch ctx 310 - rdr 311 - :: acc) 312 - [] suffix 313 - |> List.rev |> List.filter_map Fun.id 314 - in 315 327 match stdout_of_previous with 316 328 | None -> 317 329 let ctx, job = 318 - exec_process ctx job ~fds:redirect 319 - ~stdout:some_write ~pgid executable 320 - args_as_strings 330 + exec_process ctx job ~fds:rdrs ~stdout:some_write 331 + ~pgid executable args_as_strings 321 332 in 322 333 close_stdout ~is_global some_write; 323 334 loop ctx job (pgid, some_read) rest 324 335 | Some stdout -> 325 336 let ctx, job = 326 - exec_process ctx job ~fds:redirect ~stdin:stdout 337 + exec_process ctx job ~fds:rdrs ~stdin:stdout 327 338 ~stdout:some_write ~pgid executable 328 339 args_as_strings 329 340 in ··· 724 735 (ctx, acc @ word_glob_expand ctx cst)) 725 736 (ctx, []) swc 726 737 727 - and handle_built_in ~(stdout : Eio_unix.sink_ty Eio.Flow.sink) (ctx : ctx) = 728 - function 738 + and handle_built_in ~rdrs ~(stdout : Eio_unix.sink_ty Eio.Flow.sink) 739 + (ctx : ctx) = function 729 740 | Built_ins.Cd { path } -> 730 741 let cwd = S.cwd ctx.state in 731 742 let+ state = ··· 740 751 in 741 752 { ctx with state } 742 753 | Pwd -> 743 - Eio.Flow.copy_string 744 - (Fmt.str "%a\n%!" Fpath.pp (S.cwd ctx.state)) 745 - stdout; 754 + let () = 755 + Eunix.with_redirections rdrs @@ fun () -> 756 + Eio.Flow.copy_string 757 + (Fmt.str "%a\n%!" Fpath.pp (S.cwd ctx.state)) 758 + stdout 759 + in 746 760 Exit.zero ctx 747 761 | Exit n -> 748 762 let should_exit =
+1
src/lib/sast.ml
··· 99 99 | Io_op_greatand 100 100 | Io_op_dgreat 101 101 | Io_op_lessgreat 102 + | Io_op_andgreat 102 103 | Io_op_clobber 103 104 104 105 and io_file = io_op * filename
+5
test/built_ins.t
··· 143 143 test 144 144 $ msh -c "FOO=\$(pwd | rev | rev); echo \$(basename \$FOO)" 145 145 test 146 + 147 + $ sh -c "pwd > out.txt; cat out.txt | xargs -- basename" 148 + test 149 + $ msh -c "pwd > out.txt; cat out.txt | xargs -- basename" 150 + test
+6 -1
test/simple.t
··· 2 2 3 3 1. Variables and Parameters 4 4 5 - 1.1 Variable Subtitution 5 + 1.1 Variable Substitution 6 6 7 7 $ cat >test.sh <<EOF 8 8 > P="hello world" ··· 170 170 $ msh -c "echo hello 3>out.txt >&3" 171 171 $ cat out.txt 172 172 hello 173 + 174 + NON-POSIX redirection of stderr and stdout. 175 + 176 + $ msh -c "ls -j &> /dev/null" 177 + [2] 173 178 174 179 2.7 Sequences 175 180
+1
vendor/morbig.0.11.0/src/CST.mli
··· 272 272 | IoFile_GreatAnd_FileName of filename' 273 273 | IoFile_DGreat_FileName of filename' 274 274 | IoFile_LessGreat_FileName of filename' 275 + | IoFile_AndGreat_FileName of filename' 275 276 | IoFile_Clobber_FileName of filename' 276 277 277 278 and filename =
+6
vendor/morbig.0.11.0/src/parser.mly
··· 63 63 %token DGREAT LESSAND GREATAND LESSGREAT 64 64 (* '>>' '<&' '>&' '<>' *) 65 65 66 + %token ANDGREAT 67 + (* '&>' ... I know, not very POSIX *) 68 + 66 69 67 70 %token CLOBBER 68 71 (* '>|' *) ··· 451 454 } 452 455 | LESSGREAT f=located(filename) { 453 456 IoFile_LessGreat_FileName f 457 + } 458 + | ANDGREAT f=located(filename) { 459 + IoFile_AndGreat_FileName f 454 460 } 455 461 | CLOBBER f=located(filename) { 456 462 IoFile_Clobber_FileName f
+2 -2
vendor/morbig.0.11.0/src/prelexer.mll
··· 106 106 107 107 let blank = [' ' '\009' '\012'] 108 108 109 - let operator = "&&" | "||" | ";;" | 110 - "<<" | ">>" | "<&" | ">&" | "<>" | "<<-" | 109 + let operator = "&&" | "||" | ";;" | "&>" | 110 + "<<" | ">>" | "<&" | ">&" | "<>" | "<<-" | 111 111 ">|" | 112 112 "|" | "(" | ")" | "<" | ">" | ";" | "&" 113 113
+1 -1
vendor/morbig.0.11.0/src/prelexerState.ml
··· 365 365 in 366 366 let followed_by_redirection = Parser.(function 367 367 | Pretoken.Operator (LESSAND | GREATAND | DGREAT | DLESS _ 368 - | CLOBBER | LESS | GREAT | LESSGREAT) :: _ -> 368 + | CLOBBER | LESS | GREAT | LESSGREAT | ANDGREAT) :: _ -> 369 369 true 370 370 | _ -> 371 371 false
+3 -1
vendor/morbig.0.11.0/src/pretoken.ml
··· 28 28 let operators = Hashtbl.( 29 29 let t = create 17 in 30 30 List.iter (fun (sym, tok) -> add t sym tok) [ 31 - "&&", AND_IF; 32 31 "||", OR_IF; 33 32 ";;", DSEMI; 34 33 "<&", LESSAND; 35 34 ">&", GREATAND; 35 + "&>", ANDGREAT; 36 + "&&", AND_IF; 36 37 "<>", LESSGREAT; 37 38 ">>", DGREAT; 38 39 ">|", CLOBBER; ··· 64 65 | GREATAND -> ">&" 65 66 | LESSGREAT -> "<>" 66 67 | DGREAT -> ">>" 68 + | ANDGREAT -> "&>" 67 69 | CLOBBER -> ">|" 68 70 | Pipe -> "|" 69 71 | Lparen -> "("
+1
vendor/morbig.0.11.0/src/token.ml
··· 35 35 | LESSGREAT -> "LESSGREAT" 36 36 | LESSAND -> "LESSAND" 37 37 | DGREAT -> "DGREAT" 38 + | ANDGREAT -> "ANDGREAT" 38 39 | GREATAND -> "GREATAND" 39 40 | WORD w -> Printf.sprintf "WORD(%s)" (unWord w) 40 41 | ASSIGNMENT_WORD w ->