为什么使用 `printf` 加入数组会在终端和脚本中产生不同的结果?
Why does joining an array with `printf` give different results in the terminal vs a script?
我正在尝试编写一个脚本,其中涉及打印一个带有分隔符的数组。不幸的是,当 运行 作为脚本时,在命令行中输入的 printf
命令不起作用。 bash 和 zsh 处理事情的方式似乎也不同。
这给出了预期的输出,如果我像这样直接粘贴到终端就可以了:
➜ ~ array=()
array+=a
array+=b
array+=c
array+=d
printf -v tmp '%s\n' "${array[@]}"
echo "$tmp"
a
b
c
d
然而,当运行作为脚本时,输出并不是我想要的:
➜ ~ echo 'array=()
array+=a
array+=b
array+=c
array+=d
printf -v tmp '%s\n' "${array[@]}"
echo "$tmp"' > test1.sh
➜ ~ bash test1.sh
abcdn
然后我记得 zsh 是我的默认值 shell,所以我尝试 运行明确地用 zsh 连接它。结果更接近我想要的(每个数组元素之间有一些东西),但仍然不是我粘贴到终端时得到的结果。为什么在这种情况下 \n
中的 \
会被忽略?
➜ ~ zsh test1.sh
anbncndn
此外,table 基本命令(如 echo
和 printf
在 shell 和脚本与终端之间的关键区别中是否存在方便的 table 某处?
编辑:
我注意到我的示例存在缺陷,因为当我创建脚本 test.sh
文件时,echo 在 n.
之前遗漏了反斜杠
➜ ~ echo '
array=()
array+=a
array+=b
array+=c
array+=d
printf -v tmp '%s\n' "${array[@]}"
echo "$tmp"
'
array=()
array+=a
array+=b
array+=c
array+=d
printf -v tmp %sn "${array[@]}"
echo "$tmp"
但让我失望的是 zsh -c
表现出相同的行为。它是否也去除了反斜杠?
➜ ~ zsh -c 'array=()
array+=a
array+=b
array+=c
array+=d
printf -v tmp '%s\n' "${array[@]}"
echo "$tmp"'
anbncndn
编辑 2:转义字符在每种情况下都有效。
前面的三个反斜杠变成一个很好的换行符。
zsh -c 'array=()
array+=a
array+=b
array+=c
array+=d
printf -v tmp '%s\\n' "${array[@]}"
echo "$tmp"'
a
b
c
d
EDIT3:因此脚本中的转义与“-c”不同
脚本:
#!/bin/zsh
array=()
array+=a
array+=b
array+=c
array+=d
printf -v tmp '%s\n' "${array[@]}"
echo "$tmp"
结果:
➜ ~ zsh test.sh
a
b
c
d
zsh
处理数组的方式与 bash
不同。在 zsh
(您的交互式 shell)中,array+=b
确实将字符串 b
作为新的数组元素附加。
然而,在bash
中,相同的命令仅将字符串b
追加到数组的第一个元素;要追加一个新元素,您需要使用 array+=(b)
.
zsh -c 'array=()
array+=a
array+=b
array+=c
array+=d
printf -v tmp '%s\n' "${array[@]}"
echo "$tmp"'
anbncndn
在此,-c
的参数分为三部分:
'array=()
array+=a
array+=b
array+=c
array+=d
printf -v tmp '
%s\n
和
' "${array[@]}"
echo "$tmp"'
它们都连接在一起。单引号内的部分不受替换、扩展、反斜杠特殊处理等的影响。中间部分 是 ,在传递给 [=17 之前变为 %sn
=] 执行。当您添加额外的反斜杠时,%s\\n
变为 %s\n
,并且双反斜杠通过 zsh
执行命令变为单个反斜杠。
在您的脚本中,因为您没有尝试引用整个内容以使其成为一个参数,所以 printf -v tmp '%s\n' "${array[@]}"
会按照您想要的方式进行拆分和处理。但是,因为您在那里将反斜杠加倍,所以 $tmp
设置为 a\nb\nc\nd\n
。然后当你 echo
它时,默认的 zsh
行为是在正在打印的字符串中扩展反斜杠转义序列,所以它看起来是正确的。这可以通过 BSD_ECHO
shell 选项或使用 echo -E
来禁用。如果您想要实际的换行符,请在单引号内使用单个反斜杠,或者在双引号或不加引号的情况下使用双反斜杠。
示范:
$ printf -v tmp '%s\n' a b c d
$ echo "$tmp"
a
b
c
d
$ echo -E "$tmp"
a\nb\nc\nd\n
$ setopt BSD_ECHO
$ echo "$tmp"
a\nb\nc\nd\n
$ printf -v tmp '%s\n' a b c d
$ echo "$tmp"
a
b
c
d
我正在尝试编写一个脚本,其中涉及打印一个带有分隔符的数组。不幸的是,当 运行 作为脚本时,在命令行中输入的 printf
命令不起作用。 bash 和 zsh 处理事情的方式似乎也不同。
这给出了预期的输出,如果我像这样直接粘贴到终端就可以了:
➜ ~ array=()
array+=a
array+=b
array+=c
array+=d
printf -v tmp '%s\n' "${array[@]}"
echo "$tmp"
a
b
c
d
然而,当运行作为脚本时,输出并不是我想要的:
➜ ~ echo 'array=()
array+=a
array+=b
array+=c
array+=d
printf -v tmp '%s\n' "${array[@]}"
echo "$tmp"' > test1.sh
➜ ~ bash test1.sh
abcdn
然后我记得 zsh 是我的默认值 shell,所以我尝试 运行明确地用 zsh 连接它。结果更接近我想要的(每个数组元素之间有一些东西),但仍然不是我粘贴到终端时得到的结果。为什么在这种情况下 \n
中的 \
会被忽略?
➜ ~ zsh test1.sh
anbncndn
此外,table 基本命令(如 echo
和 printf
在 shell 和脚本与终端之间的关键区别中是否存在方便的 table 某处?
编辑:
我注意到我的示例存在缺陷,因为当我创建脚本 test.sh
文件时,echo 在 n.
➜ ~ echo '
array=()
array+=a
array+=b
array+=c
array+=d
printf -v tmp '%s\n' "${array[@]}"
echo "$tmp"
'
array=()
array+=a
array+=b
array+=c
array+=d
printf -v tmp %sn "${array[@]}"
echo "$tmp"
但让我失望的是 zsh -c
表现出相同的行为。它是否也去除了反斜杠?
➜ ~ zsh -c 'array=()
array+=a
array+=b
array+=c
array+=d
printf -v tmp '%s\n' "${array[@]}"
echo "$tmp"'
anbncndn
编辑 2:转义字符在每种情况下都有效。 前面的三个反斜杠变成一个很好的换行符。
zsh -c 'array=()
array+=a
array+=b
array+=c
array+=d
printf -v tmp '%s\\n' "${array[@]}"
echo "$tmp"'
a
b
c
d
EDIT3:因此脚本中的转义与“-c”不同
脚本:
#!/bin/zsh
array=()
array+=a
array+=b
array+=c
array+=d
printf -v tmp '%s\n' "${array[@]}"
echo "$tmp"
结果:
➜ ~ zsh test.sh
a
b
c
d
zsh
处理数组的方式与 bash
不同。在 zsh
(您的交互式 shell)中,array+=b
确实将字符串 b
作为新的数组元素附加。
然而,在bash
中,相同的命令仅将字符串b
追加到数组的第一个元素;要追加一个新元素,您需要使用 array+=(b)
.
zsh -c 'array=()
array+=a
array+=b
array+=c
array+=d
printf -v tmp '%s\n' "${array[@]}"
echo "$tmp"'
anbncndn
在此,-c
的参数分为三部分:
'array=()
array+=a
array+=b
array+=c
array+=d
printf -v tmp '
%s\n
和
' "${array[@]}"
echo "$tmp"'
它们都连接在一起。单引号内的部分不受替换、扩展、反斜杠特殊处理等的影响。中间部分 是 ,在传递给 [=17 之前变为 %sn
=] 执行。当您添加额外的反斜杠时,%s\\n
变为 %s\n
,并且双反斜杠通过 zsh
执行命令变为单个反斜杠。
在您的脚本中,因为您没有尝试引用整个内容以使其成为一个参数,所以 printf -v tmp '%s\n' "${array[@]}"
会按照您想要的方式进行拆分和处理。但是,因为您在那里将反斜杠加倍,所以 $tmp
设置为 a\nb\nc\nd\n
。然后当你 echo
它时,默认的 zsh
行为是在正在打印的字符串中扩展反斜杠转义序列,所以它看起来是正确的。这可以通过 BSD_ECHO
shell 选项或使用 echo -E
来禁用。如果您想要实际的换行符,请在单引号内使用单个反斜杠,或者在双引号或不加引号的情况下使用双反斜杠。
示范:
$ printf -v tmp '%s\n' a b c d
$ echo "$tmp"
a
b
c
d
$ echo -E "$tmp"
a\nb\nc\nd\n
$ setopt BSD_ECHO
$ echo "$tmp"
a\nb\nc\nd\n
$ printf -v tmp '%s\n' a b c d
$ echo "$tmp"
a
b
c
d