简体   繁体   English

为什么用`printf`加入数组会在终端和脚本中给出不同的结果?

[英]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 和脚本与终端之间的echoprintf等基本命令的主要区别是否有一个方便的表格?

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM