Different (supposedly) POSIX compliant shell environments yield varied results when asked to emit non-printable characters. But there is one method that works consistently across a wide variety of shells.
Sparked by this conversation.
Given
hex='\x40\x00\x40'
hex2='\\x40\\x00\\x40'
oct='\100\000\100'
oct2='\0100\0000\0100'you get this:
| dashᵃ | dashᵇ | ksh | bash | BusyBox sh | zsh | |
|---|---|---|---|---|---|---|
echo -n "$hex" |
$hex¹ | $hex | $hex | $hex | $hex | @␀@¹ |
printf "$hex"² |
$hex | $hex | @␀@ |
@␀@ |
@␀@ |
@␀@ |
printf '%b' "$hex"³ |
$hex | $hex | $hex | @␀@ |
@␀@ |
@␀@ |
echo -n "$hex2" |
$hex | $hex | $hex2 | $hex2 | $hex2 | $hex |
printf "$hex2" |
$hex | $hex | $hex | $hex | $hex | $hex |
printf '%b' "$hex2" |
$hex | $hex | $hex | $hex | $hex | $hex |
echo -n "$oct" |
@␀@ |
@ |
$oct | $oct | $oct | \100␀\100¹ |
printf "$oct"* |
@␀@ |
@␀@ |
@␀@ |
@␀@ |
@␀@ |
@␀@ |
printf '%b' "$oct" |
@␀@ |
@ |
\100␀\100 |
@␀@ |
@␀@ |
\100␀\100 |
echo -n "$oct2" |
@␀@ |
@ |
$oct2 | $oct2 | $oct2 | @␀@ |
printf "$oct2" |
␈0␀0␈0¹ |
␈0␀0␈0 |
␈0␀0␈0 |
␈0␀0␈0 |
␈0␀0␈0 |
␈0␀0␈0 |
printf '%b' "$oct2" |
@␀@ |
@ |
@␀@ |
@␀@ |
@␀@ |
@␀@ |
- ᵃ These results are from running dash in an Ubuntu Docker container.
- ᵇ This is from running dash on my host system, where it apparently interpreted the octal sequence as a C style (zero-terminated) string.
- ¹ Italicized $var name stands for the unchanged literal value of the corresponding variable;
@␀@represents the expected byte sequence;
␀stands for the null character (00) and␈stands for the backspace character (08);. - ² A POSIX-compliant
printfis not supposed to recognize "hexadecimal character constants as defined in the ISO C standard". - ³ The
bconversion specifier character is supposed to only convert\0dddoctal sequences, according to the POSIX spec. - * The only one that yields consistent results —
printf "$oct".
The only approach that works consistently across all shells is using the octal escape sequence as the printf format string (printf '\100\000\100').