name: "Builtins: mapfile" cases: # # Basic functionality # - name: "mapfile basic" stdin: | mapfile arr < <(echo -e "a\nb\nc") declare -p arr - name: "mapfile -t" stdin: | mapfile -t arr < <(echo -e "a\nb\nc") declare -p arr - name: "readarray -t" stdin: | readarray -t arr < <(echo -e "a\nb\nc") declare -p arr - name: "mapfile default MAPFILE" stdin: | mapfile -t < <(echo -e "a\nb") declare -p MAPFILE # # -O flag # - name: "mapfile -O starts at offset" stdin: | mapfile -t -O 3 arr < <(echo -e "a\nb\nc") declare -p arr - name: "mapfile -O 0 preserves existing" stdin: | arr=(x y z w) mapfile -t -O 0 arr < <(echo -e "a\nb") declare -p arr - name: "mapfile without -O clears existing" stdin: | arr=(x y z w) mapfile -t arr < <(echo -e "a\nb") declare -p arr - name: "mapfile -O preserves sparse array" stdin: | declare -a arr arr[0]=x arr[5]=y arr[10]=z mapfile -t -O 2 arr < <(echo -e "a\nb") declare -p arr - name: "mapfile -O large offset" stdin: | mapfile -t -O 100 arr < <(echo -e "a\nb") declare -p arr - name: "mapfile -O negative" ignore_stderr: true stdin: | mapfile -O -1 arr < /dev/null echo "exit: $?" # # Variable state edge cases # - name: "mapfile into nonexistent variable" stdin: | mapfile -t newvar < <(echo -e "a\nb") declare -p newvar - name: "mapfile into string variable" stdin: | myvar="hello" mapfile -t myvar < <(echo -e "a\nb") declare -p myvar - name: "mapfile -O into string variable" stdin: | myvar="hello" mapfile -t -O 2 myvar < <(echo -e "a\nb") declare -p myvar - name: "mapfile into associative array" ignore_stderr: true stdin: | declare -A myassoc mapfile -t myassoc < <(echo -e "a\nb") echo "exit: $?" # # -d delimiter # - name: "mapfile -d custom delimiter" stdin: | mapfile -t -d 'Z' arr < <(echo -ne "helloZthereZgeneralZkenobi") declare -p arr - name: "mapfile -d empty string (NUL)" stdin: | mapfile -t -d '' arr < <(printf 'a\0b\0c') declare -p arr # # -n count # - name: "mapfile -n limits count" stdin: | mapfile -t -n 2 arr < <(echo -e "a\nb\nc\nd") declare -p arr - name: "mapfile -n 0 no limit" stdin: | mapfile -t -n 0 arr < <(echo -e "a\nb\nc") declare -p arr # # -s skip # - name: "mapfile -s" stdin: | mapfile -t -s 2 arr < <(echo -e "a\nb\nc\nd") declare -p arr - name: "mapfile -s with -n" stdin: | mapfile -t -s 1 -n 2 arr < <(echo -e "a\nb\nc\nd\ne") declare -p arr # # -u file descriptor # - name: "mapfile -u reads from fd" stdin: | exec 3< <(echo -e "a\nb\nc") mapfile -t -u 3 arr exec 3<&- declare -p arr - name: "mapfile -u invalid fd" ignore_stderr: true stdin: | exec 99<&- # fd 99 must be closed, but the harness can leak one in mapfile -u 99 arr echo "exit: $?" # # Combinations # - name: "mapfile -O with -s and -n" stdin: | mapfile -t -O 10 -s 1 -n 2 arr < <(echo -e "a\nb\nc\nd\ne") declare -p arr - name: "mapfile multiple invocations without -O" stdin: | mapfile -t arr < <(echo -e "a\nb") mapfile -t arr < <(echo -e "x\ny\nz") declare -p arr - name: "mapfile multiple invocations with -O" stdin: | mapfile -t arr < <(echo -e "a\nb") mapfile -t -O 2 arr < <(echo -e "x\ny\nz") declare -p arr # # Empty input # - name: "mapfile empty input" stdin: | mapfile -t arr < /dev/null declare -p arr 2>/dev/null || echo "arr not set" - name: "mapfile empty input clears existing" stdin: | arr=(x y z) mapfile -t arr < /dev/null declare -p arr 2>/dev/null || echo "arr not set" - name: "mapfile -O empty input preserves" stdin: | arr=(x y z) mapfile -t -O 0 arr < /dev/null declare -p arr - name: "mapfile into nameref variable" known_failure: true stdin: | declare -a target declare -n ref=target mapfile -t ref < <(echo -e "a\nb\nc") declare -p target - name: "mapfile -O into nameref variable" known_failure: true stdin: | target=(x y) declare -n ref=target mapfile -t -O 2 ref < <(echo -e "a\nb") declare -p target # Sent by another program, so that it reaches the shell through the kernel; nothing is ever # written to the FIFO, so the read ends only if the signal ends it. - name: "A signal that ends the shell ends a mapfile waiting for input" known_failure: true # TODO(signals): signals aren't delivered to the shell timeout_in_seconds: 5 stdin: | mkfifo p; exec 3<>p trap 'echo exit-trap' EXIT ( sleep 0.2; /bin/kill -TERM $$ ) & mapfile -t -n 1 a <&3 echo "not reached" # # Option parsing # - name: "mapfile options given more than once keep the last" stdin: | mapfile -t -n 1 -n 2 arr <<< $'a\nb\nc'; declare -p arr mapfile -t -s 1 -s 0 arr <<< $'a\nb'; declare -p arr arr=(x y z); mapfile -t -O 2 -O 1 arr <<< $'a\nb'; declare -p arr mapfile -t -d , -d . arr <<< 'a.b,c'; declare -p arr - name: "mapfile with combined and attached short options" stdin: | mapfile -tn2 arr <<< $'a\nb\nc'; declare -p arr mapfile -td, arr <<< 'a,b'; declare -p arr mapfile -t -u3 arr 3<<< $'a\nb'; declare -p arr arr=(x y z); mapfile -t -O+1 arr <<< $'a\nb'; declare -p arr mapfile -t -n +2 arr <<< $'a\nb\nc'; declare -p arr mapfile -t -d - arr <<< 'a-b'; declare -p arr - name: "mapfile with -- before the array name" stdin: | mapfile -t -- arr <<< $'a\nb'; declare -p arr - name: "mapfile with an unknown option or missing value fails with status 2" ignore_stderr: true stdin: | mapfile -Z arr <<< a; echo "rc=$?" mapfile -t -O; echo "rc=$?" - name: "mapfile with an invalid count fails with status 1" known_failure: true # TODO(mapfile): fails with status 2 ignore_stderr: true stdin: | mapfile -t -n abc arr <<< a; echo "rc=$?" mapfile -t -n -1 arr <<< a; echo "rc=$?" mapfile -t -s -1 arr <<< a; echo "rc=$?" mapfile -t -s abc arr <<< a; echo "rc=$?" mapfile -t -O abc arr <<< a; echo "rc=$?" mapfile -t -c 0 arr <<< a; echo "rc=$?" mapfile -t -c abc arr <<< a; echo "rc=$?" mapfile -t -u -1 arr <<< a; echo "rc=$?" - name: "mapfile checks every count, origin and descriptor it is given" ignore_stderr: true stdin: | # Only whether it fails is compared: the status itself is a known failure. exec 99<&- # fd 99 must be closed, but the harness can leak one in arr=(keep); mapfile -t -s-1 -s0 arr <<< data; echo "failed=$(($? != 0)) ${arr[*]}" arr=(keep); mapfile -t -c0 -c5000 arr <<< data; echo "failed=$(($? != 0)) ${arr[*]}" arr=(keep); mapfile -t -n-1 -n1 arr <<< data; echo "failed=$(($? != 0)) ${arr[*]}" arr=(keep); mapfile -t -O-1 -O0 arr <<< data; echo "failed=$(($? != 0)) ${arr[*]}" arr=(keep); mapfile -t -u 99 -u 0 arr <<< data; echo "failed=$(($? != 0)) ${arr[*]}" arr=(keep); mapfile -t -s0 -s1 arr <<< $'a\nb'; echo "failed=$(($? != 0)) ${arr[*]}" - name: "mapfile -C calls back for each group of lines" known_failure: true # TODO(mapfile): -C and -c are not implemented stdin: | cb() { echo "cb $*"; } mapfile -t -C cb -c 1 arr <<< $'a\nb'; declare -p arr - name: "mapfile ignores words after the array name" known_failure: true # TODO(mapfile): an extra word is an error, and later options are parsed ignore_stderr: true stdin: | mapfile -t arr extra <<< a; echo "rc=$?" mapfile -t arr -n 1 <<< $'a\nb'; declare -p arr - name: "mapfile into an invalid name fails" known_failure: true # TODO(mapfile): an invalid name is accepted ignore_stderr: true stdin: | mapfile -t 1bad <<< a; echo "rc=$?" mapfile -t -- -x <<< a; echo "rc=$?" - name: "mapfile option values may begin with a hyphen" known_failure: true # TODO(mapfile): such a value is taken for an option stdin: | mapfile -t -d -- arr <<< 'a--b'; declare -p arr mapfile -t -s -0 arr <<< $'a\nb'; declare -p arr