#!/bin/sh # shellcheck disable=SC2004,SC2015,SC2016,SC2034,SC2086,SC2091,SC2123,SC2181,SC2194 # Bugs and compatibility check for shell # This script is for development purposes. # It provide as is, do not any support. # It may change without notice. # Example of use # contrib/bugs.sh # bash contrib/bugs.sh # contrib/test_in_docker.sh [Dockerfile] -- bash contrib/bugs.sh affect() { echo "[affect] $title"; } no_problem() { echo "[------] $title"; } skip() { echo "[skip ] $title [skip reason: $1]"; } if { file=$(mktemp -t tmp.XXXXXX); } 2>/dev/null; then maketemp() { mktemp -t tmp.XXXXXX; } rm "$file" else maketemp() { mktemp tmp.XXXXXX; } fi ( title="01: set -e option is reset (posh)" set -eu foo() { eval 'return 0'; } foo case $- in *e*) no_problem ;; *) affect ;; esac ) ( title="02: double quoted string treated as a pattern (posh)" string="abc[d]" pattern="c[d]" case $string in *"$pattern"*) no_problem ;; *) affect ;; esac ) ( title="03: @: parameter not set (posh, ksh <= around 93s)" if (set -u; : "$@") 2>/dev/null; then no_problem; else affect; fi ) return_true() { false; } ( title="04: function not overrite (ksh)" return_true() { true; } if return_true; then no_problem; else affect; fi ) ( title="05: exit not return exit code (zsh <= around 4.2.5)" foo() { (exit 123) &&:; }; foo if [ $? -eq 123 ]; then no_problem; else affect; fi ) ( title="06: abort if unset not the defined variable (bash <= around 2.05a, zsh <= around 4.2.5, ksh <= around 93s)" ok=$( set -e unset not_defined_variable echo 1 ) if [ "$ok" ]; then no_problem; else affect; fi ) ( title="07: limited arithmetic expansion (dash <= around 0.5.4, old ash)" i=0 ok=$(eval 'i=$((i+1))' 2>/dev/null; echo 1) if [ "$ok" ]; then no_problem; else affect; fi ) ( title="08: Unsupported trap (posh: around 0.8.5)" if (trap '' INT) 2>/dev/null; then no_problem; else affect; fi ) ( title="09: different split by IFS (zsh, posh <= around 0.6.13)" [ "${ZSH_VERSION:-}" ] && emulate -R sh string="1,2," IFS=',' set -- $string if [ $# -eq 2 ]; then no_problem; else affect; fi ) ( title="10: variable reference in the same line (dash <= around 0.5.5)" i=1 i=$(($i+1)) j=$i if [ "$j" -eq 2 ]; then no_problem; else affect; fi ) ( title="11: command time -p output LF at first (solaris)" if (command time echo) >/dev/null 2>/dev/null; then command time -p printf '' 2>&1 | { IFS= read -r line if [ "$line" ]; then no_problem; else affect; fi } else skip 'time command not found' fi ) ( title="12: cat not ignore set -e (zsh = around 5.4.2)" ( set -e nagative() { false; } if false; then :; else nagative && : || :; fi ) [ $? -eq 0 ] && no_problem || affect ) ( title="13: many unset cause a Segmentation fault (mksh = around 39)" ( i=0 while [ $i -lt 30000 ]; do unset v ||: i=$(($i+1)) done ) 2>/dev/null [ $? -eq 0 ] && no_problem || affect ) ( title="14: here document does not expand parameters (ash = around 0.3.8)" set -- value foo() { cat; } result=$(foo<"$file" ret=$(cat "$file") rm "$file" case $ret in *bad\ nzombie*) affect ;; *) no_problem ;; esac ) ( title='23: can not read after reading null character (yash = around 2.46)' file=$(maketemp) printf 'foo\0bar' > "$file" IFS= read -r ret < "$file" IFS= read -r ret </dev/null) ||: [ "$ch" = "A" ] && no_problem || affect ) ( title='26: Segmentation fault in eval (ksh 93q, 93r)' ( ( str="" i=0 step1() { if (eval ':' 2>/dev/null); then :; fi return 0 } step2() { set --; eval "$(:)"; } step3() { eval "str=\${str#}"; } step1 step2 while [ $i -lt 100 ]; do step3 # 10 Segmentation fault "$@ i=$(($i+1)) done ) & wait $! # wait is not related this bug, it just prevent fail. ) 2>/dev/null [ $? = 0 ] && no_problem || affect ) ( title='27: set -C not working (posh = around 0.10.2)' file=$(maketemp) (set -C; : > "$file") 2>/dev/null &&: ret=$? rm "$file" [ $ret -ne 0 ] && no_problem || affect ) ( title='28: variable expansion not working within the double qoute (posh >= 0.8.5)' a='foobar' b='bar' [ "${a%"$b"*}" = "foo" ] && no_problem || affect ) ( title='29: case will be aborted when exit status is error (mksh <= 35.2, pdksh 5.2.14 on debian 2.2)' foo() { set -e; case 1 in (*) false &&: ;; esac; echo ok; } ret=$(foo) [ "$ret" = "ok" ] && no_problem || affect ) ( title='30: eval interpret invalid -- option (posh = around 0.3.14)' ret=$(eval echo -- foo) [ "$ret" = "-- foo" ] && no_problem || affect ) ( title='31: ${v%%=*} not working (zsh <= around 4.3.17)' kv="key=value" (ret="${kv%%=*}"; [ "$ret" = "key" ]) 2>/dev/null &&: [ $? = 0 ] && no_problem || affect ) ( title='32: last readonly always display "is read only" in subshell (ksh 93q, 93r)' ( # readonly A # => not display # readonly B # => display "B is read only" readonly A; (readonly B) 2>/dev/null [ $? = 0 ] && no_problem || affect ) &&: ) 2>/dev/null ( title='33: fails when use readonly with value (pdksh 5.2.14 on debian 2.2)' message=$( ( readonly value=123 ) 2>&1 ) [ "$message" ] && affect || no_problem ) ( title='34: built-in commands can not redefine (busybox = around 1.1.3)' whoami() { echo nobody; } [ "$(whoami)" = nobody ] && no_problem || affect ) ( title='35: exit status is not 127 when command not found (zsh <= around 4.0.4)' no-such-command 2>/dev/null &&: [ $? = 127 ] && no_problem || affect ) ( title='36: exit status is not 127 when command not found and PATH='' (bash <= 4.3)' no-such-command 2>/dev/null &&: if [ $? = 1 ]; then skip 'affected #35' else set -e PATH='' no-such-command 2>/dev/null &&: [ $? = 127 ] && no_problem || affect fi ) ( title='37: wrong parse command substitution (bosh/posh = 20181030,20190311)' $( cat << HERE HERE ) ret=$( printf a; printf b ) [ "$ret" = "ab" ] && no_problem || affect ) ( title='38: Freeze in pipe processing (bosh/posh = 20181030,20190311)' printf '1\n2\n' | cat | while read -r line; do # /usr/bin/printf '%s' "$line" # freeze printf '%s' "$line" # not freeze done > /dev/null [ -e /usr/bin/printf ] && printf=/usr/bin/printf || printf=/bin/printf file=$(maketemp) # Code for detection. Output one line only. { echo 1; echo 2; } | { while IFS= read -r line; do echo "$line" done | cat | while read -r line; do $printf '%s' "$line" done } > $file ret=$(cat "$file") rm "$file" [ "$ret" = "1" ] && affect || no_problem ) ( title='39: can not set -e options with eval (pdksh, mksh, posh)' set -e eval "set +e" case $- in *e*) affect ;; *) no_problem ;; esac ) ( title='40: can not return exit status from subshell (bash 4.1.5, 4.2)' ret() { return $1; } foo() { set -e; ret 123; } set +e (foo) [ $? = 123 ] && no_problem || affect ) echo Done