[英]Why does joining an array with `printf` give different results in the terminal vs a script?
I was trying to write a script which involved printing an array with a delimiter.我正在尝试编写一个脚本,其中涉及打印带有分隔符的数组。 Unfortunately, the printf
command that worked typing into the command line did not work when run as a script.不幸的是,当作为脚本运行时,在命令行中输入的printf
命令不起作用。 It also seems that bash and zsh handle things differently.似乎 bash 和 zsh 处理事情的方式也不同。
This gives the intended output, it works if I just paste directly into the terminal like this:这给出了预期的 output,如果我像这样直接粘贴到终端中,它就可以工作:
➜ ~ array=()
array+=a
array+=b
array+=c
array+=d
printf -v tmp '%s\n' "${array[@]}"
echo "$tmp"
a
b
c
d
However, when run as a script, the output was not what I wanted:但是,当作为脚本运行时,output 不是我想要的:
➜ ~ echo 'array=()
array+=a
array+=b
array+=c
array+=d
printf -v tmp '%s\n' "${array[@]}"
echo "$tmp"' > test1.sh
➜ ~ bash test1.sh
abcdn
Then I remembered that zsh is my default shell, so I tried running it explicitly with zsh.然后我记得zsh是我默认的shell,所以我尝试用zsh显式运行它。 The results were closer to what I wanted (there was something in between each array element), but still not what I got when pasting into the terminal.结果更接近我想要的(每个数组元素之间都有一些东西),但仍然不是我粘贴到终端时得到的结果。 Why is the \
in \n
ignored in this case?为什么在这种情况下忽略\
in \n
?
➜ ~ zsh test1.sh
anbncndn
Also, is there a handy table somewhere of the key differences in basic commands like echo
and printf
between shells and in scripts vs the terminal?此外,shell 和脚本与终端之间的echo
和printf
等基本命令的主要区别是否有一个方便的表格?
EDIT:编辑:
I noticed that my example was flawed in that when I created the script test.sh
file, the echo left out the backslash before the n.我注意到我的示例存在缺陷,因为当我创建脚本test.sh
文件时,回显在 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"
But what threw me off was that zsh -c
showed the same behavior.但让我失望的是zsh -c
表现出相同的行为。 Is it also stripping out backslashes?它是否也去掉了反斜杠?
➜ ~ zsh -c 'array=()
array+=a
array+=b
array+=c
array+=d
printf -v tmp '%s\n' "${array[@]}"
echo "$tmp"'
anbncndn
Edit 2: It's that escape characters work in each of these situations.编辑 2:在这些情况下,转义字符都起作用。 three preceding backslashes turn in back into a good ol' newline.三个前面的反斜杠又变成了一个很好的换行符。
zsh -c 'array=()
array+=a
array+=b
array+=c
array+=d
printf -v tmp '%s\\\\n' "${array[@]}"
echo "$tmp"'
a
b
c
d
EDIT3: So the escaping in a script works differently from '-c' EDIT3:因此脚本中的 escaping 与“-c”的工作方式不同
The script:剧本:
#!/bin/zsh
array=()
array+=a
array+=b
array+=c
array+=d
printf -v tmp '%s\\n' "${array[@]}"
echo "$tmp"
the result:结果:
➜ ~ zsh test.sh
a
b
c
d
zsh
handles arrays differently than bash
. zsh
处理 arrays 与bash
不同。 In zsh
(your interactive shell), array+=b
really does append the string b
as a new array element.在zsh
(您的交互式 shell)中, array+=b
确实 append 字符串b
作为新的数组元素。
In bash
, however, the same command only appends the string b
to the first element of the array;然而,在bash
中,相同的命令仅将字符串b
附加到数组的第一个元素; to append a new element, you need to use array+=(b)
.到 append 一个新元素,你需要使用array+=(b)
。
zsh -c 'array=()
array+=a
array+=b
array+=c
array+=d
printf -v tmp '%s\n' "${array[@]}"
echo "$tmp"'
anbncndn
In this, the argument to -c
is split into three parts:在此, -c
的参数分为三个部分:
'array=()
array+=a
array+=b
array+=c
array+=d
printf -v tmp '
%s\n
and和
' "${array[@]}"
echo "$tmp"'
which are all concatenated together.它们都连接在一起。 The sections inside single quotes aren't subject to substitutions, expansions, special handling of backslashes, etc. The middle section is , and becomes %sn
before it's passed to zsh
for execution.单引号内的部分不受替换、扩展、反斜杠的特殊处理等的影响。中间部分是, 并在传递给zsh
执行之前变为%sn
。 When you add extra backslashes, %s\\\\n
becomes %s\\n
, and the double backslash is turned into a single one by the zsh
executing the command.当您添加额外的反斜杠时, %s\\\\n
变为%s\\n
,并且zsh
执行命令将双反斜杠变为单个反斜杠。
In your script, because you're not trying to quote the entire thing to make it a single argument, printf -v tmp '%s\\n' "${array[@]}"
is split and treated the way you want.在您的脚本中,因为您不想引用整个内容以使其成为单个参数, printf -v tmp '%s\\n' "${array[@]}"
被拆分并按照您想要的方式处理. However, because you're doubling up the backslash there, $tmp
is set to a\nb\nc\nd\n
.但是,因为您在那里将反斜杠加倍,所以$tmp
设置为a\nb\nc\nd\n
。 Then when you echo
it, the default zsh
behavior is to expand backslash escape sequences in the string being printed, so it looks right.然后当你echo
显它时,默认的zsh
行为是在正在打印的字符串中扩展反斜杠转义序列,所以它看起来是正确的。 This can be disabled with the BSD_ECHO
shell option, or using echo -E
.这可以使用BSD_ECHO
shell 选项或使用echo -E
禁用。 If you want actual newlines, use a single backslash inside single quotes, or a double one inside double quotes or unquoted.如果您想要实际的换行符,请在单引号内使用单个反斜杠,或在双引号内使用双反斜杠或不加引号。
Demonstration:示范:
$ 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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.