536 lines
14 KiB
YAML
536 lines
14 KiB
YAML
name: "Builtins: printf"
|
|
|
|
cases:
|
|
# Basic functionality tests
|
|
- name: "One-variable printf"
|
|
stdin: |
|
|
printf "%s" "0"
|
|
|
|
- name: "Basic printf"
|
|
stdin: |
|
|
printf "%s, %s" "Hello" "world"
|
|
|
|
- name: "printf -v"
|
|
stdin: |
|
|
printf -v myvar "%s, %s" "Hello" "world"
|
|
echo "myvar: '${myvar}'"
|
|
|
|
- name: "printf -v with array index"
|
|
stdin: |
|
|
declare -a myarray=()
|
|
printf -v 'myarray[5]' "%s, %s" "Hello" "world"
|
|
declare | grep myarray
|
|
|
|
- name: "printf with -v as a format arg"
|
|
stdin: |
|
|
printf "%s\n" "-v"
|
|
|
|
- name: "printf -v with attached value"
|
|
stdin: |
|
|
printf -va ""
|
|
echo "a: '${a}'"
|
|
printf -vmyvar "%s, %s" "Hello" "world"
|
|
echo "myvar: '${myvar}'"
|
|
|
|
- name: "printf -vv with attached value"
|
|
stdin: |
|
|
printf -vva "value"
|
|
echo "va: '${va}'"
|
|
|
|
- name: "printf with no-consuming format and extra arguments"
|
|
stdin: |
|
|
printf "foo" bar baz qux
|
|
echo
|
|
printf "no specifiers\n" 1 2 3
|
|
|
|
- name: "printf format string starting with hyphen via --"
|
|
stdin: |
|
|
printf -- "-5\n"
|
|
printf -- "%s\n" "-v"
|
|
|
|
- name: "printf with hyphen-prefixed format arguments"
|
|
stdin: |
|
|
printf "%s\n" -5
|
|
printf "%d\n" -5
|
|
printf "%s %s\n" -a -b
|
|
printf "%s\n" -v
|
|
printf "%s\n" --foo
|
|
printf "%s\n" -
|
|
printf "x%sy\n" -abc
|
|
printf "%d %d\n" -1 -2 -3 -4
|
|
|
|
- name: "printf with option-like format arguments and attached values"
|
|
stdin: |
|
|
printf "%s\n" "-v" "-va" "-vva"
|
|
|
|
- name: "printf with -- among format arguments"
|
|
stdin: |
|
|
printf "%s\n" -- -x
|
|
printf "%s|%s\n" a -- b
|
|
printf -- "%s\n" -- x
|
|
|
|
- name: "printf with hyphen-prefixed format string (invalid option)"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
printf -x
|
|
echo "Result: $?"
|
|
|
|
# Bash-specific %q format (shell-safe quoting)
|
|
- name: "printf %q"
|
|
stdin: |
|
|
echo "[1]"
|
|
printf "%q" 'TEXT'; echo
|
|
|
|
echo "[2]"
|
|
printf "%q" '$VAR'; echo
|
|
|
|
echo "[3]"
|
|
printf "%q" '"'; echo
|
|
|
|
- name: "printf ~%q"
|
|
stdin: |
|
|
echo "[1]"
|
|
printf "~%q" 'TEXT'; echo
|
|
|
|
echo "[2]"
|
|
printf "~%q" '$VAR'; echo
|
|
|
|
echo "[3]"
|
|
printf "~%q" '"'; echo
|
|
|
|
# =================================================================
|
|
# STANDARD C PRINTF FORMAT SPECIFIERS
|
|
# These should work with external printf and bash builtin printf
|
|
# =================================================================
|
|
|
|
# Integer formatting tests
|
|
- name: "printf %d with integers"
|
|
stdin: |
|
|
printf "%d\n" 42
|
|
printf "%d\n" -17
|
|
printf "%d\n" 0
|
|
|
|
- name: "printf %o (octal)"
|
|
stdin: |
|
|
printf "%o\n" 42
|
|
printf "%o\n" 8
|
|
printf "%o\n" 255
|
|
|
|
- name: "printf %x and %X (hex)"
|
|
stdin: |
|
|
printf "%x\n" 255
|
|
printf "%X\n" 255
|
|
printf "%x\n" 42
|
|
|
|
# Floating point tests
|
|
- name: "printf %f (float)"
|
|
stdin: |
|
|
printf "%f\n" 3.14159
|
|
printf "%.2f\n" 3.14159
|
|
printf "%6.2f\n" 3.14159
|
|
|
|
- name: "printf %e and %E (scientific)"
|
|
stdin: |
|
|
printf "%e\n" 1234.5
|
|
printf "%E\n" 1234.5
|
|
|
|
- name: "printf %g and %G (general)"
|
|
stdin: |
|
|
printf "%g\n" 1234.5
|
|
printf "%G\n" 0.00012345
|
|
|
|
# Character formatting
|
|
- name: "printf %c (character)"
|
|
stdin: |
|
|
printf "%c\n" 65
|
|
printf "%c\n" 97
|
|
printf "%c%c%c\n" 72 101 121
|
|
|
|
# Width and precision tests
|
|
- name: "printf width formatting"
|
|
stdin: |
|
|
printf "%5s|\n" "hi"
|
|
printf "%-5s|\n" "hi"
|
|
printf "%05d|\n" 42
|
|
|
|
- name: "printf precision with strings"
|
|
stdin: |
|
|
printf "%.3s\n" "hello"
|
|
printf "%10.3s\n" "hello"
|
|
|
|
# Multiple arguments and reuse
|
|
- name: "printf argument reuse"
|
|
stdin: |
|
|
printf "%s %s %s\n" "one" "two"
|
|
printf "%d %d %d\n" 1 2
|
|
|
|
# Special characters and escapes
|
|
- name: "printf with escape sequences"
|
|
stdin: |
|
|
printf "hello\nworld\n"
|
|
printf "tab\there\n"
|
|
printf "quote: \"\n"
|
|
|
|
- name: "printf with backslash escapes"
|
|
stdin: |
|
|
printf "\\n\\t\\r\\\\\n"
|
|
|
|
# Missing arguments (bash handles gracefully)
|
|
- name: "printf missing arguments"
|
|
stdin: |
|
|
printf "%s %s\n" "only_one"
|
|
|
|
# Empty and edge cases
|
|
- name: "printf with empty format"
|
|
stdin: |
|
|
printf ""
|
|
|
|
- name: "printf with just format string"
|
|
stdin: |
|
|
printf "just text\n"
|
|
|
|
# Type conversion tests (safe cases only)
|
|
- name: "printf valid string to number conversion"
|
|
stdin: |
|
|
printf "%d\n" "42"
|
|
printf "%d\n" "0"
|
|
|
|
- name: "printf with percent literal"
|
|
stdin: |
|
|
printf "100%% complete\n"
|
|
printf '%%q\n'
|
|
|
|
- name: "printf %b (backslash escape expansion)"
|
|
known_failure: true
|
|
stdin: |
|
|
printf "%b\n" "hello\\nworld"
|
|
printf "%b\n" "tab\\there"
|
|
printf "%b\n" "quote\\\"here"
|
|
|
|
- name: "printf %b with field width"
|
|
known_failure: true
|
|
stdin: |
|
|
printf "%10b|\n" "hello\\nworld"
|
|
printf "%-10b|\n" "tab\\there"
|
|
|
|
- name: "printf %T (date/time formatting)"
|
|
known_failure: true # Not supported by uucore
|
|
stdin: |
|
|
printf "%(%Y-%m-%d)T\n" 1234567890
|
|
|
|
# Test argument reuse with bash-specific formats
|
|
- name: "printf %q argument reuse"
|
|
stdin: |
|
|
printf "%q %q %q\n" "one with spaces" "two"
|
|
|
|
- name: "printf %b argument reuse"
|
|
stdin: |
|
|
printf "%b %b %b\n" "one\\ntwo" "three"
|
|
|
|
# Test C-style constant interpretation
|
|
- name: "printf with quoted character arguments"
|
|
known_failure: true
|
|
stdin: |
|
|
printf "%d\n" "'A"
|
|
printf "%d\n" '"'
|
|
|
|
- name: "printf with plus/minus signs in numeric arguments"
|
|
stdin: |
|
|
printf "%d\n" "+42"
|
|
printf "%d\n" "-17"
|
|
|
|
# =================================================================
|
|
# ADDITIONAL STANDARD FORMAT SPECIFIERS
|
|
# More comprehensive coverage of printf(3) man page features
|
|
# =================================================================
|
|
|
|
# Additional format specifiers from man page
|
|
- name: "printf %i (signed integer)"
|
|
stdin: |
|
|
printf "%i\n" 42
|
|
printf "%i\n" -17
|
|
|
|
- name: "printf %u (unsigned integer)"
|
|
stdin: |
|
|
printf "%u\n" 42
|
|
printf "%u\n" 4294967295
|
|
|
|
# Advanced width and precision
|
|
- name: "printf with asterisk width and precision"
|
|
stdin: |
|
|
printf "%*s|\n" 10 "hello"
|
|
printf "%.*s|\n" 3 "hello world"
|
|
printf "%*.*s|\n" 10 3 "hello world"
|
|
|
|
- name: "printf with zero precision and zero arg"
|
|
stdin: |
|
|
printf "%.0d\n" 0
|
|
|
|
- name: "printf with zero precision"
|
|
stdin: |
|
|
printf "%.0d\n" 42
|
|
|
|
# Flag characters
|
|
- name: "printf with flag characters"
|
|
stdin: |
|
|
printf "%+d\n" 42
|
|
printf "% d\n" 42
|
|
printf "%#x\n" 255
|
|
printf "%#o\n" 64
|
|
|
|
# Left justification
|
|
- name: "printf left justification"
|
|
stdin: |
|
|
printf "%-10s|\n" "left"
|
|
printf "%-10d|\n" 42
|
|
|
|
# Zero padding
|
|
- name: "printf zero padding"
|
|
stdin: |
|
|
printf "%010d\n" 42
|
|
printf "%010.2f\n" 3.14
|
|
|
|
# Combining flags
|
|
- name: "printf combining flags"
|
|
stdin: |
|
|
printf "%+010d\n" 42
|
|
printf "%-+10d|\n" 42
|
|
|
|
# More precision tests
|
|
- name: "printf precision with integers"
|
|
stdin: |
|
|
printf "%.5d\n" 42
|
|
printf "%.5o\n" 42
|
|
printf "%.5x\n" 42
|
|
|
|
# Large numbers and edge cases
|
|
- name: "printf with large numbers"
|
|
stdin: |
|
|
printf "%d\n" 2147483647
|
|
printf "%u\n" 4294967295
|
|
printf "%x\n" 4294967295
|
|
|
|
# More character and escape testing
|
|
- name: "printf with special characters"
|
|
stdin: |
|
|
printf "%c\n" 0
|
|
printf "%c\n" 32
|
|
printf "%c\n" 127
|
|
|
|
# Negative numbers with different formats
|
|
- name: "printf negative numbers"
|
|
stdin: |
|
|
printf "%d\n" -1
|
|
printf "%o\n" -1
|
|
printf "%x\n" -1
|
|
printf "%u\n" -1
|
|
|
|
# Empty string handling
|
|
- name: "printf with empty strings"
|
|
stdin: |
|
|
printf "%s|\n" ""
|
|
printf "%5s|\n" ""
|
|
printf "%.5s|\n" ""
|
|
|
|
# More empty string handling
|
|
- name: "printf with empty strings for numbers"
|
|
known_failure: true
|
|
stdin: |
|
|
printf "%d" ""
|
|
printf "%x" ""
|
|
|
|
# Scientific notation edge cases
|
|
- name: "printf scientific notation edge cases"
|
|
stdin: |
|
|
printf "%e\n" 0.0
|
|
printf "%E\n" 0.0
|
|
printf "%g\n" 0.0000001
|
|
printf "%G\n" 1000000.0
|
|
|
|
# Mixed format strings
|
|
- name: "printf mixed formats in one string"
|
|
stdin: |
|
|
printf "Dec: %d, Hex: %x, Oct: %o, Char: %c\n" 65 65 65 65
|
|
|
|
# Field width larger than content
|
|
- name: "printf wide fields"
|
|
stdin: |
|
|
printf "%20s|\n" "short"
|
|
printf "%20d|\n" 42
|
|
|
|
# Return value testing (should always be 0 for successful printf)
|
|
- name: "printf return value"
|
|
stdin: |
|
|
printf "test"
|
|
echo " exit: $?"
|
|
|
|
# Error handling tests - marked as known failures due to external printf differences
|
|
- name: "printf with invalid format"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
printf "%z\n" "test"
|
|
|
|
- name: "printf with no arguments"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
printf
|
|
echo "Result: $?"
|
|
|
|
- name: "printf string to number conversion with invalid input"
|
|
known_failure: true # Need to investigate
|
|
stdin: |
|
|
printf "%d\n" "abc"
|
|
echo "Result: $?"
|
|
|
|
- name: "printf %q with empty string"
|
|
stdin: |
|
|
printf '%q\n' ''
|
|
printf '~%q\n' ''
|
|
|
|
- name: "printf -v through nameref"
|
|
known_failure: true
|
|
stdin: |
|
|
declare -n ref=target
|
|
printf -v ref "formatted %d" 42
|
|
echo "target: $target"
|
|
echo "ref: $ref"
|
|
|
|
- name: "printf %q with multiple args and mixed specs"
|
|
stdin: |
|
|
printf '%q\n' 'foo bar'
|
|
printf '%q ' 'a b' '' "x'y" 'a,b' '/tmp/x-[1]/y' $'a\nb'; echo
|
|
printf '%s=%q\n' k 'v w' m 'n$o'
|
|
printf '~%q\n' 'a b'
|
|
|
|
- name: "printf %q shell-sensitive characters"
|
|
stdin: |
|
|
printf '%q\n' '~' 'x:~' 'x=~' '#' 'x#y'
|
|
printf '%Q\n' '~' 'x:~' 'x=~' '#' 'x#y'
|
|
|
|
- name: "printf %Q"
|
|
stdin: |
|
|
printf '%Q\n' 'foo bar'
|
|
|
|
- name: "printf %q with ignored modifier"
|
|
stdin: |
|
|
printf '<%2q>\n' 'foo bar'
|
|
|
|
- name: "printf %q width and precision semantics"
|
|
known_failure: true # Requires q/Q modifier metadata from uucore
|
|
stdin: |
|
|
printf '<%12q>\n' 'foo bar'
|
|
printf '<%.5q>\n' 'foo bar'
|
|
printf '<%.5Q>\n' 'foo bar'
|
|
|
|
- name: "printf %q dynamic and positional modifiers"
|
|
known_failure: true # Dynamic modifiers are unsupported; positional forms follow uucore
|
|
ignore_stderr: true
|
|
stdin: |
|
|
printf '<%*q>\n' 12 'foo bar'
|
|
printf '<%.*Q>\n' 5 'foo bar'
|
|
printf '<%2$q>\n' first second
|
|
|
|
- name: "printf %q alternate form"
|
|
min_oracle_version: "5.3" # Alternate form forces single quoting starting in bash 5.3
|
|
stdin: |
|
|
printf '<%#q>\n' 'foo bar'
|
|
printf '<%#Q>\n' 'foo bar'
|
|
|
|
- name: "printf %q alternate form edge cases"
|
|
min_oracle_version: "5.3" # Alternate form forces single quoting starting in bash 5.3
|
|
known_failure: true # Requires alternate-form and width metadata from uucore
|
|
stdin: |
|
|
printf '<%#q>\n' 'foo'
|
|
printf '<%#12q>\n' 'foo bar'
|
|
|
|
- name: "printf %q escapes commas"
|
|
stdin: |
|
|
printf '%q\n' 'a,b' ',' '{a,b}'
|
|
|
|
- name: "printf -v followed by --"
|
|
stdin: |
|
|
printf -v x -- '%s' ho
|
|
echo "x=$x"
|
|
printf -vy -- '%s-' -- a
|
|
echo "y=$y"
|
|
|
|
- name: "printf -v given more than once keeps the last"
|
|
stdin: |
|
|
printf -v x -v y '%s' z; echo "x=[$x] y=[$y] rc=$?"
|
|
printf -vp -vq '%s' r; echo "p=[$p] q=[$q] rc=$?"
|
|
|
|
- name: "printf -v leaves later option-like words as data"
|
|
stdin: |
|
|
printf -v x '%s,' -v y -- -x; echo "x=[$x] y=[$y]"
|
|
printf -v x -- -- a; echo "x=[$x]"
|
|
|
|
- name: "printf -v with no format fails"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
printf -v x; echo "rc=$?"
|
|
printf -v x --; echo "rc=$?"
|
|
printf -v; echo "rc=$?"
|
|
|
|
- name: "printf -v with an invalid name fails with status 2"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
printf -v 1bad '%s' x; echo "rc=$?"
|
|
printf -v -x '%s' x; echo "rc=$?"
|
|
printf -v -- '%s' x; echo "rc=$?"
|
|
printf -v-x '%s' x; echo "rc=$?"
|
|
printf -v 'a b' '%s' x; echo "rc=$?"
|
|
printf -v '' '%s' x; echo "rc=$?"
|
|
printf -v 'a[' '%s' x; echo "rc=$?"
|
|
printf -v 'a[]' '%s' x; echo "rc=$?"
|
|
declare -p a 2>/dev/null || echo "a unset"
|
|
# The name is checked before the format.
|
|
printf -v 1bad '%d' abc; echo "rc=$?"
|
|
|
|
- name: "printf -v rejects a subscript with text after its closing bracket"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
printf -v 'a[0]]' '%s' x; echo "rc=$?"
|
|
printf -v 'a[0]junk]' '%s' x; echo "rc=$?"
|
|
declare -p a 2>/dev/null || echo "a unset"
|
|
printf -v 'a[x[1]]' '%s' x; echo "nested: rc=$?"
|
|
declare -A m; printf -v 'm["]"]' '%s' x; echo "quoted: rc=$? ${m["]"]}"
|
|
|
|
- name: "printf -v checks every name it is given"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
x=old; printf -v -x -v x '%s' new; echo "rc=$? x=$x"
|
|
x=old; printf -v 1bad -v x '%s' new; echo "rc=$? x=$x"
|
|
x=old; printf -v x -v 1bad '%s' new; echo "rc=$? x=$x"
|
|
printf -v y -v x '%s' last; echo "rc=$? x=$x y=${y-unset}"
|
|
|
|
- name: "printf -v doesn't evaluate a subscript to check the name"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
x=old; printf -v 'a[\]]' -v x '%s' new; echo "rc=$? x=$x"
|
|
x=old; printf -v "a['x]']" -v x '%s' new; echo "rc=$? x=$x"
|
|
x=old; printf -v 'a[$(echo ])]' -v x '%s' new; echo "rc=$? x=$x"
|
|
x=old; printf -v 'a["x]' -v x '%s' new; echo "rc=$? x=$x"
|
|
|
|
- name: "printf -v checks a subscript's brackets as an assignment does"
|
|
known_failure: true # TODO(parser): an assignment's subscript must balance ( and may hold an unquoted [
|
|
ignore_stderr: true
|
|
stdin: |
|
|
x=old; printf -v 'a[(]' -v x '%s' new; echo "rc=$? x=$x"
|
|
x=old; printf -v 'a[[]' -v x '%s' new; echo "rc=$? x=$x"
|
|
|
|
- name: "printf -v takes a[@] and a[*] as names but can't assign to them"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
x=old; printf -v 'a[@]' -v x '%s' new; echo "rc=$? x=$x"
|
|
x=old; printf -v 'a[*]' -v x '%s' new; echo "rc=$? x=$x"
|
|
printf -v 'a[@]' '%s' v; echo "rc=$? ${a[*]-unset}"
|
|
printf -v 'a[*]' '%s' v; echo "rc=$? ${a[*]-unset}"
|
|
|
|
- name: "printf --help prints help on stdout"
|
|
known_failure: true # TODO(printf): --help is taken for an invalid option
|
|
stdin: |
|
|
printf --help >/dev/null 2>&1; echo "rc=$?"
|
|
[[ -n $(printf --help 2>/dev/null) ]] && echo "help on stdout"
|
|
printf -v x --help >/dev/null 2>&1; echo "after -v: rc=$?"
|
|
printf -- --help; echo " after --: rc=$?"
|