name: "Builtins: exec" cases: - name: "Exec an invalid path" ignore_stderr: true stdin: | exec /some/nonexistent/path - name: "Exec with no arguments" stdin: | pid=$$ exec [[ "${pid}" == "$$" ]] || echo "PID changed" - name: "Exec with no arguments and redirection for new fd" stdin: | exec 3>some-file.txt echo "Hello, fd 3!" >&3 - name: "Exec with no arguments and redirection of stderr" stdin: | exec 2>/dev/null # Do something that should display an error to stderr source /non-existent - name: "Exec with no arguments and redirection in a script" test_files: - path: "script.sh" contents: | exec 2>output-file.txt echo hi >&2 args: ["./script.sh"] - name: "Exec a command" stdin: | exec $0 -c 'echo "In child shell"' echo "This is never reached" - name: "exec without -c" stdin: | export myvar=value exec $0 -c 'echo "myvar: ${myvar}"' - name: "exec -c" stdin: | export myvar=value exec -c $0 -c 'echo "myvar: ${myvar}"' - name: "exec -a" stdin: | exec -a shellname $0 -c 'echo "0: $0"' - name: "exec -c of a command found in the shell's PATH" test_files: - path: "bin/myscript" executable: true contents: | #!/bin/sh echo "in myscript" stdin: | PATH="${PWD}/bin:${PATH}" exec -c myscript echo "not reached" - name: "exec of a missing command exits the shell" known_failure: true # TODO(exec): a failed exec doesn't end a non-interactive shell ignore_stderr: true stdin: | (exec nonexistent-command; echo "subshell still running: $?") echo "subshell: $?" exec /nonexistent-command echo "still running: $?" - name: "exec in a subshell ends the subshell" known_failure: true # TODO(exec): exec in a subshell doesn't end the subshell stdin: | (exec true; echo "still running") echo "true: $?" (exec sh -c 'exit 3'; echo "still running") echo "exit 3: $?" (exec -a renamed sh -c 'echo "argv0: $0"') (exec -l sh -c 'echo "login argv0: $0"') echo hello | (exec cat; echo "still running") - name: "exec in a subshell runs the program, not a builtin" known_failure: true # TODO(exec): exec in a subshell runs a builtin of the same name instead ignore_stderr: true stdin: | # The printf builtin would print nothing; the program prints "-v". (exec printf -v out hi) - name: "exec of a missing program reports it" known_failure: true # TODO(exec): exec of a program that can't be found reports nothing at the top level stdin: | "$0" -c 'exec ./no-such-program' 2> err.txt echo "status: $?" [[ -s err.txt ]] && echo "reported" || echo "silent" # (What it says has the test's directory in it, which differs between shells.) rm err.txt - name: "exec starts a program by its full path" known_failure: true # TODO(exec): exec starts a program by the path it was found by, not its full path, so a script sees that as its $0 test_files: - path: "sub/prog" executable: true contents: | #!/bin/sh echo "$0" stdin: | cd sub started_by=$("$0" -c 'exec ./prog') [[ ${started_by} == "${PWD}/prog" ]] && echo "full path" || echo "other: ${started_by}" - name: "exec -a takes a name that starts with -" stdin: | exec -a -shellname $0 --noprofile -c 'echo "0: $0"' - name: "exec passes the command's options through" stdin: | (exec -- echo a) (exec echo -a b) (exec echo -- c) # A top-level exec replaces the shell with the program, so its arguments # take the launch path rather than the builtin echo. printf '#!/bin/sh\nprintf "[%%s]" "$@"; echo\n' > args; chmod +x args "$0" --noprofile --norc -c 'exec -- ./args a' "$0" --noprofile --norc -c 'exec ./args -a b' "$0" --noprofile --norc -c 'exec ./args -- c' rm args - name: "exec rejects an unknown option with status 2" stdin: | (exec -x echo hi) 2>/dev/null; echo "rc=$?"