mod common;
shell_compat! {
name: braced_last_exit_code_after_success,
script: "true; echo ${?}",
eq: "0",
}
shell_compat! {
name: braced_last_exit_code_after_failure,
script: "false; echo ${?}",
eq: "1",
}
shell_compat! {
name: exit_code_in_arithmetic,
script: "false; echo $(( $? + 10 ))",
eq: "11",
}
shell_compat! {
name: exit_code_in_arithmetic_after_success,
script: "true; echo $(( $? * 5 ))",
eq: "0",
}
shell_compat! {
name: braced_exit_code_in_arithmetic,
script: "false; echo $(( ${?} + 5 ))",
eq: "6",
}
shell_compat! {
name: exit_code_in_string_interpolation,
script: r#"false; echo "exit code: $?""#,
eq: "exit code: 1",
}
shell_compat! {
name: braced_exit_code_in_string_interpolation,
script: r#"false; echo "exit code: ${?}""#,
eq: "exit code: 1",
}
shell_compat! {
name: nested_command_substitution,
script: "echo $(echo $(echo hello))",
eq: "hello",
}
shell_compat! {
name: deeply_nested_command_substitution,
script: "echo $(echo $(echo $(echo deep)))",
eq: "deep",
}
shell_compat! {
name: cmd_subst_with_true,
script: "VAR=$(true); echo \"exit: $?\"",
eq: "exit: 0",
}
shell_compat! {
name: cmd_subst_with_false,
script: "VAR=$(false); echo \"exit: $?\"",
eq: "exit: 1",
}
shell_compat! {
name: cmd_subst_in_string,
script: r#"echo "inner: $(echo nested)""#,
eq: "inner: nested",
}
shell_compat! {
name: cmd_subst_in_string_with_var,
script: r#"VAL=world; echo "hello $(echo $VAL)""#,
eq: "hello world",
}
shell_compat! {
name: var_in_default_with_cmd_subst,
script: r#"echo "${UNSET:-$(echo computed)}""#,
eq: "computed",
}
shell_compat! {
name: return_does_not_leak_to_capture,
script: "f() {\n echo output\n return 5\n}\nX=$(f)\necho \"captured: [$X]\"\n",
contains: "captured: [output]",
absent: "captured: [5",
}
shell_compat! {
name: return_sets_exit_code,
script: "f() { return 42; }\nf\necho $?\n",
eq: "42",
}
shell_compat! {
name: cmd_subst_in_return,
script: "f() { return $(echo 42); }\nf\necho $?\n",
eq: "42",
}
shell_compat! {
name: return_without_value,
script: "f() { echo hi; return; }\nX=$(f)\necho \"got: [$X]\"\n",
eq: "got: [hi]",
}
shell_compat! {
name: positional_params_arithmetic_add,
script: "add() {\n echo $(($1 + $2))\n}\nadd 3 4\n",
eq: "7",
}
shell_compat! {
name: positional_params_arithmetic_mul,
script: "mul() {\n echo $(($1 * $2))\n}\nmul 5 6\n",
eq: "30",
}
shell_compat! {
name: export_with_value,
script: "export FOO=\"bar\"; echo $FOO",
eq: "bar",
}
shell_compat! {
name: export_multiple_vars,
script: "export A=1; export B=2; echo \"$A $B\"",
eq: "1 2",
}
shell_compat! {
name: nested_var_default_all_unset,
script: r#"echo "${A:-${B:-deep}}""#,
eq: "deep",
}
shell_compat! {
name: nested_var_default_outer_set,
script: r#"A=outer; echo "${A:-${B:-deep}}""#,
eq: "outer",
}
shell_compat! {
name: nested_var_default_middle_set,
script: r#"B=middle; echo "${A:-${B:-deep}}""#,
eq: "middle",
}
shell_compat! {
name: deeply_nested_var_defaults,
script: r#"echo "${A:-${B:-${C:-deepest}}}""#,
eq: "deepest",
}
shell_compat! {
name: cmd_subst_in_test_eq,
script: r#"[[ $(echo hello) == "hello" ]] && echo "match" || echo "nope""#,
eq: "match",
}
shell_compat! {
name: cmd_subst_in_test_neq,
script: r#"[[ $(echo hello) != "world" ]] && echo "diff" || echo "same""#,
eq: "diff",
}
shell_compat! {
name: cmd_subst_in_case,
script: "case $(echo hello) in\n hello) echo \"matched\" ;;\n *) echo \"nope\" ;;\nesac\n",
eq: "matched",
}
shell_compat! {
name: compound_and_both_true,
script: r#"[[ -d / && -d /tmp ]] && echo "both""#,
eq: "both",
}
shell_compat! {
name: compound_and_one_false,
script: r#"[[ -d / && -f /nonexistent_kaish_test ]] && echo "both" || echo "failed""#,
eq: "failed",
}
shell_compat! {
name: compound_or_first_true,
script: r#"[[ -d / || -f /nonexistent_kaish_test ]] && echo "one""#,
eq: "one",
}
shell_compat! {
name: compound_or_second_true,
script: r#"[[ -f /nonexistent_kaish_test || -d / ]] && echo "one""#,
eq: "one",
}
shell_compat! {
name: compound_or_both_false,
script: r#"[[ -f /nonexistent_kaish_test || -f /also_nonexistent_kaish ]] && echo "yes" || echo "no""#,
eq: "no",
}
shell_compat! {
name: compound_not_true,
script: r#"[[ ! -f /nonexistent_kaish_test ]] && echo "correct""#,
eq: "correct",
}
shell_compat! {
name: compound_not_false,
script: r#"[[ ! -d / ]] && echo "yes" || echo "no""#,
eq: "no",
}
shell_compat! {
name: compound_double_not,
script: r#"[[ ! ! -d / ]] && echo "yes" || echo "no""#,
eq: "yes",
}
shell_compat! {
name: compound_string_tests,
script: r#"VAR=""; [[ -z "$VAR" || -n "default" ]] && echo "ok""#,
eq: "ok",
}
shell_compat! {
name: compound_with_comparison,
script: r#"X=5; [[ $X -gt 3 && $X -lt 10 ]] && echo "in range""#,
eq: "in range",
}
shell_compat! {
name: compound_in_if,
script: "X=5\nif [[ $X -gt 0 && $X -lt 10 ]]; then\n echo valid\nelse\n echo invalid\nfi\n",
eq: "valid",
}
shell_compat! {
name: numeric_ge_multidigit_unquoted,
script: "X=15; [[ $X -ge 2 ]] && echo ok || echo nope",
eq: "ok",
}
shell_compat! {
name: numeric_ge_multidigit_quoted,
script: r#"X=15; [[ "$X" -ge 2 ]] && echo ok || echo nope"#,
eq: "ok",
}
shell_compat! {
name: numeric_ge_string_literals,
script: r#"[[ "15" -ge "2" ]] && echo ok || echo nope"#,
eq: "ok",
}
shell_compat! {
name: numeric_lt_multidigit_via_cmd_subst,
script: r#"COUNT=$(echo 5); [[ "$COUNT" -lt 10 ]] && echo ok || echo nope"#,
eq: "ok",
}
shell_compat! {
name: numeric_gt_two_quoted_strings,
script: r#"[[ "15" -gt "2" ]] && echo ok || echo nope"#,
eq: "ok",
}
shell_compat! {
name: numeric_le_two_quoted_strings,
script: r#"[[ "2" -le "15" ]] && echo ok || echo nope"#,
eq: "ok",
}
shell_compat! {
name: numeric_eq_leading_zero_string,
script: r#"[[ "01" -eq "1" ]] && echo ok || echo nope"#,
eq: "ok",
}
shell_compat! {
name: numeric_ne_quoted_strings,
script: r#"[[ "2" -ne "15" ]] && echo ok || echo nope"#,
eq: "ok",
}
shell_compat! {
name: string_eq_leading_zero_vs_int_literal,
script: r#"X="01"; [[ "$X" == 1 ]] && echo same || echo diff"#,
eq: "diff",
}
shell_compat! {
name: string_eq_leading_zero_vs_quoted,
script: r#"X="01"; [[ "$X" == "1" ]] && echo same || echo diff"#,
eq: "diff",
}
shell_compat! {
name: string_eq_quoted_vs_int_literal_agree,
script: r#"X="1"; [[ "$X" == 1 ]] && [[ "$X" == "1" ]] && echo agree || echo split"#,
eq: "agree",
}
shell_compat! {
name: numeric_eq_handles_what_string_eq_does_not,
script: r#"X="01"; [[ "$X" -eq 1 ]] && echo numeric || echo lex"#,
eq: "numeric",
}
shell_compat! {
name: compound_short_circuit_or,
script: r#"[[ -d / || $(cat /nonexistent_file) == "x" ]] && echo "yes" || echo "no""#,
eq: "yes",
}
shell_compat! {
name: compound_short_circuit_and,
script: r#"[[ -f /nonexistent_kaish_test && $(cat /nonexistent_file) == "x" ]] && echo "yes" || echo "no""#,
eq: "no",
}
shell_compat! {
name: compound_not_with_and,
script: r#"[[ ! -f /nonexistent_kaish_test && -d / ]] && echo "both""#,
eq: "both",
}
shell_compat! {
name: compound_precedence,
script: r#"[[ -f /nonexistent_kaish_test || -d / && -d /tmp ]] && echo "yes" || echo "no""#,
eq: "yes",
}
shell_compat! {
name: colon_in_unquoted_arg,
script: "echo foo::bar",
eq: "foo::bar",
}
shell_compat! {
name: colon_port_in_arg,
script: "echo host:8080",
eq: "host:8080",
}
shell_compat! {
name: colon_in_variable_assignment,
script: "PATH=/usr/bin:/usr/local/bin\necho $PATH",
eq: "/usr/bin:/usr/local/bin",
}
shell_compat! {
name: pipeline_large_output_no_deadlock,
script: "seq 1 20000 | wc -l",
contains: "20000",
}
shell_compat! {
name: head_n_in_pipeline,
script: "printf 'a\\nb\\nc\\nd\\ne\\n' | head -n 3",
eq: "a\nb\nc",
}
shell_compat! {
name: pipeline_three_stage_large_output_no_deadlock,
script: "seq 1 20000 | grep '1' | wc -l",
eq: "13439",
}
shell_compat! {
name: cmd_subst_variable_isolation,
script: "X=outer\nset_inner() { X=inner; echo $X; }\nY=$(set_inner)\necho $X\n",
eq: "outer",
}
shell_compat! {
name: for_loop_cleanup_after_body_failure,
script: "for i in 1 2 3; do\n false\ndone\nfor j in a b c; do\n echo $j\ndone\n",
eq: "a\nb\nc",
}
shell_compat! {
name: errexit_after_and_chain,
script: "set -e\ntrue && true\nfalse\necho \"should not reach\"\n",
absent: "should not reach",
}
shell_compat! {
name: errexit_after_or_chain,
script: "set -e\nfalse || true\nfalse\necho \"should not reach\"\n",
absent: "should not reach",
}
shell_compat! {
name: errexit_suppressed_inside_and_chain_left,
script: "set -e\nfalse && echo right\necho reached\n",
contains: "reached",
absent: "right",
}
shell_compat! {
name: false_exits_one,
script: "false",
exit: 1,
}
shell_compat! {
name: explicit_exit_code,
script: "exit 7",
exit: 7,
}
shell_compat! {
name: command_subst_no_implicit_split,
script: "for x in $(echo \"a b c\"); do echo \"[$x]\"; done",
kaish_eq: "[a b c]",
bash_eq: "[a]\n[b]\n[c]",
}
shell_compat! {
name: for_loop_variable_scoping,
script: "for i in 1 2 3; do\n true\ndone\necho \"after: [$i]\"\n",
kaish_eq: "after: []",
bash_eq: "after: [3]",
}