bash 中 printf 的默认 FORMAT 字符串是什么?

What is the default FORMAT string of printf in bash?

我正在尝试编写一个脚本来根据提供的 ByteString 值计算 base32 字符串,这些值是我作为键值对获得的。 ByteString 使用八进制转义序列和反斜杠转义序列。

考虑这个脚本:

#! /bin/bash

LINE='  bytes: "LaPaLaPa3\""'

echo $LINE

K="${LINE%%: *}"
V="${LINE#*: }"
V="${V#\"}"
V="${V%\"}"      
K="${K^^}"

echo "KV='${K}'='${V}'"

FOO="$(printf "%b" "${V}")"
echo "=========================================="
printf "${FOO}" | wc -c
printf "${FOO}" | od -bc -tu1 -w24
printf "${FOO}" | base32 | tr -d "="
echo "Correct or at least wanted result!"
echo "------------------------------------------"
printf '%s' "${FOO}" | wc -c
printf '%s' "${FOO}" | od -bc -tu1 -w24
printf '%s' "${FOO}" | base32 | tr -d "="
echo "------------------------------------------"
printf '%b' "${FOO}" | wc -c
printf '%b' "${FOO}" | od -bc -tu1 -w24
printf '%b' "${FOO}" | base32 | tr -d "="
echo "------------------------------------------"
printf "%s" "${FOO}" | wc -c
printf "%s" "${FOO}" | od -bc -tu1 -w24
printf "%s" "${FOO}" | base32 | tr -d "="
echo "------------------------------------------"
printf "%b" "${FOO}" | wc -c
printf "%b" "${FOO}" | od -bc -tu1 -w24
printf "%b" "${FOO}" | base32 | tr -d "="

我从中得到这个输出:

bytes: "LaPaLaPa3\""
KV='  BYTES'='LaPaLaPa3\"'
==========================================
10
0000000 114 141 120 141 114 141 120 141 363 042
          L   a   P   a   L   a   P   a 363   "
         76  97  80  97  76  97  80  97 243  34
0000012
JRQVAYKMMFIGD4ZC
Correct or at least wanted result!
------------------------------------------
11
0000000 114 141 120 141 114 141 120 141 363 134 042
          L   a   P   a   L   a   P   a 363   \   "
         76  97  80  97  76  97  80  97 243  92  34
0000013
JRQVAYKMMFIGD424EI
------------------------------------------
11
0000000 114 141 120 141 114 141 120 141 363 134 042
          L   a   P   a   L   a   P   a 363   \   "
         76  97  80  97  76  97  80  97 243  92  34
0000013
JRQVAYKMMFIGD424EI
------------------------------------------
11
0000000 114 141 120 141 114 141 120 141 363 134 042
          L   a   P   a   L   a   P   a 363   \   "
         76  97  80  97  76  97  80  97 243  92  34
0000013
JRQVAYKMMFIGD424EI
------------------------------------------
11
0000000 114 141 120 141 114 141 120 141 363 134 042
          L   a   P   a   L   a   P   a 363   \   "
         76  97  80  97  76  97  80  97 243  92  34
0000013
JRQVAYKMMFIGD424EI

好的,如果第一个结果看起来可行,为什么我不直接使用它呢?

嗯,一个原因是 printf 不应该在没有 FORMAT 字符串的情况下使用,我猜是因为 printf 应该有一些 FORMAT 字符串,这似乎是默认使用(?)并完成我想要的? 另一个原因是我有其他 ByteString,只有当我没有提供任何 FORMAT 字符串(printf: ...: invalid format character)时我才会出错,我认为当 ByteString 中有百分比字符时会发生这种情况,但我不是确定在这一点上,我没有正确的例子,不幸的是,它重现了这一点。 所以为了安全起见,我必须提供一个 FORMAT 字符串,对吗?但是正如您所看到的,当我尝试其他一些 FORMAT 字符串时,我得到了这个示例的错误结果!?!?

所以如果有一个 FORMAT 字符串适用于任何情况,那么我可以只使用这个字符串,但到目前为止我没有找到任何默认值?

那么 printf bash 内置函数的默认值 FORMAT 是多少?

编辑 我的问题的标题是非常详细的回答,所以首先要感谢你。我已经学会了仔细检查概要,这样我就可以自己弄明白了。问题有点复杂,因为我将八进制转义和反斜杠转义混合在一起。但是,如果我在某处使用双引号来自动插入 ByteString,那么这将插入八进制值,因为它只会转义三位数字中的第一位。所以双引号内的两个字符或字节 "3\"" 会变成 363" 所以我会得到它的 4 个字符/字节 3,6,3 和一个双引号而不是八进制值的字符363 后跟一个双引号!所以我想我的问题(现在我对 printf 了解更多并且上游推出了非标准的 ByteStrings)现在哪个是最好的/故障安全策略?以某种方式首先转换/转换八进制转义序列是否有意义?然后让 bash (我假设它是 bash 在双引号“”之间进行插值?)对剩余的反斜杠转义进行插值?或者我将如何分两步执行此操作?我在脚本中使用 printf '%s' 或 '%b' 尝试的策略到目前为止没有成功,我现在不知道如何让它工作。

所以总而言之,我想这里正确的策略是通过用相应的字符替换八进制转义符来减少值,或者在第一步中可能使用标准反斜杠转义符,这样结果就可以进一步被内插由 bash 本身放在双引号之间?这是正确的吗?如果是,该怎么做?

编辑2 正如亚伦在评论中所建议的那样,我试图提出一个解决方案,即使用 printf FORMAT 字符串 %b 将八进制转义序列转换为字符,然后紧接着步骤对结果进行转换,我将所有出现的 \" 替换为单个双引号 ".

printf '%b' "${FOO}" | sed 's|\"|"|g' | wc -c
printf '%b' "${FOO}" | sed 's|\"|"|g' | od -bc -tu1 -w24
printf '%b' "${FOO}" | sed 's|\"|"|g' | base32 | tr -d "="
10
0000000 114 141 120 141 114 141 120 141 363 042
          L   a   P   a   L   a   P   a 363   "
         76  97  80  97  76  97  80  97 243  34
0000012
JRQVAYKMMFIGD4ZC

这似乎有效,因为我在这种情况下得到的结果是正确的。

我希望这在每种情况下都能产生正确的结果...

printf 不能在没有格式字符串的情况下使用:当您使用单个参数调用它时,该参数将被解析为格式。

man bash 中考虑它的概要:

printf [-v var] format [arguments]

参数列表是可选的,而不是格式。

man bash 继续说在格式字符串中找到的纯字符被原样复制到输出流,这就是为什么你可以像 echo 'message' 一样使用 printf 'message' .

但是,它还补充说,它将识别字符转义序列以在打印前转换它们(这类似于 echo -e 所做的),最重要的是,“格式化字符序列”(%X 子字符串),它将被(可能已转换的)附加参数替换,或者如果没有剩余参数可供使用,则为默认值。

这就是您不应该 printf "$message" 的原因:您的 $message 可能包含将由 printf.

解释的字符序列

如果你想按原样打印消息,你会想使用 printf '%s' "$message",其中 %s 是要求 printf 输出(文本)的格式说明符参数作为文本(因此,不加修改地输出)。