带有额外空格的多行字符串(保留缩进)
Posted
技术标签:
【中文标题】带有额外空格的多行字符串(保留缩进)【英文标题】:Multi-line string with extra space (preserved indentation) 【发布时间】:2014-07-18 17:11:46 【问题描述】:我想用以下内容将一些预定义的文本写入文件:
text="this is line one\n
this is line two\n
this is line three"
echo -e $text > filename
我期待这样的事情:
this is line one
this is line two
this is line three
但是得到了这个:
this is line one
this is line two
this is line three
我肯定每个\n
后面都没有空格,但是多余的空格是怎么出来的呢?
【问题讨论】:
我不确定但是.. 如果您只是在同一行中输入text="this is line one\nthis is line two\nthis is line three"
会怎样..? (没有任何输入)
去掉每一行的\n
,你已经打了换行符移动到新行
你已经给了\n
。那么为什么你把下一行换成新行呢?只需text="this is line one\nthis is line two\nthis is line three"
删除每行末尾的\n
会导致输出全部在一行上运行。
啊哈:在 echo 行中的 "$text"
周围加上双引号至关重要。没有它们,任何换行符(文字和'\n')都不起作用。有了他们,他们都可以。
【参考方案1】:
难以阅读:
This 看起来太 bash 了 :) (难以阅读)我的一般口味:
cat << EndOfMessage
This is line 1.
This is line 2.
Line 3.
EndOfMessage
更好,更易于阅读:
让我们来点 Pythonic(这仍然是 bash):
text="this is line one
this is line two
this is line three\n"
dedent text
printf "$text" # print to screen
printf "$text" > file.txt # print to a file
啊……这样更好。 :) 这让我想起了 Python 的 textwrap.dedent()
函数 which I use here。
下面是神奇的dedent
函数的样子:
dedent()
local -n reference="$1"
reference="$(echo "$reference" | sed 's/^[[:space:]]*//')"
样本输出到屏幕:
this is line one
this is line two
this is line three
如果不首先调用dedent text
,输出将如下所示:
this is line one
this is line two
this is line three
变量text
被传递给dedent
通过引用,这样内部函数修改的内容会影响外部变量函数。
更多细节和解释和参考,在这里查看我的另一个答案:Equivalent of python's textwrap dedent in bash
你最初的尝试有问题
OP 的引用(加上我的重点):
我肯定每个
\n
后面都没有空格,但是多余的空格是怎么出来的?
你最初的尝试是这样的:
text="this is line one\n
this is line two\n
this is line three"
echo -e $text
...但是您的输出在第 2 行和第 3 行之前有一个额外的空间。为什么?
通过推理和实验,我的结论是echo
将行尾的实际换行符(您实际按下Enter时得到的换行符)转换为空格。因此,该空格会显示在文本中每个 \n
的 之后 行之前。
因此,解决方案是在每行末尾添加一个反斜杠 \
在字符串引号内的任何行末尾处转义真正的换行符,如下所示:
text="this is line one\n\
this is line two\n\
this is line three"
echo -e "$text"
不要在后面的反斜杠之前放置一个空格(例如:text="this is line one\n \
),否则该空格将直接返回到您的输出中,并导致您在多余的空格上出现同样的问题! p>
或者,只需将我的技术与上面的 dedent
函数一起使用,它还具有能够缩进代码的附加功能,看起来非常漂亮、美观和可读。
【讨论】:
【参考方案2】:或者使用空格缩进文本:
#!/bin/sh
sed 's/^[[:blank:]]*//' >filename <<EOF
this is line one
this is line two
this is line three
EOF
相同但使用变量:
#!/bin/sh
text="$(sed 's/^[[:blank:]]*//' << whatever
this is line one
this is line two
this is line three
)"
echo "$text" > filename
;-)
【讨论】:
【参考方案3】:我听说正在寻找这个答案,但也想将它传递给另一个命令。给定的答案是正确的,但如果有人想管道它,你需要像这样在多行字符串之前管道它
echo | tee /tmp/pipetest << EndOfMessage
This is line 1.
This is line 2.
Line 3.
EndOfMessage
这将允许您拥有多行字符串,但也可以将其放入后续命令的标准输入中。
【讨论】:
【参考方案4】:有很多方法可以做到这一点。对我来说,将缩进的字符串输送到 sed 效果很好。
printf_strip_indent()
printf "%s" "$1" | sed "s/^\s*//g"
printf_strip_indent "this is line one
this is line two
this is line three" > "file.txt"
此答案基于 Mateusz Piotrowski 的答案,但有所改进。
【讨论】:
【参考方案5】:Heredoc 听起来更方便。它用于将多个命令发送到命令解释程序,如 ex 或 cat
cat << EndOfMessage
This is line 1.
This is line 2.
Line 3.
EndOfMessage
<<
后面的字符串表示停止位置。
要将这些行发送到文件,请使用:
cat > $FILE <<- EOM
Line 1.
Line 2.
EOM
您也可以将这些行存储到一个变量中:
read -r -d '' VAR << EOM
This is line 1.
This is line 2.
Line 3.
EOM
这会将行存储到名为 VAR
的变量中。
打印时,请记住变量周围的引号,否则您将看不到换行符。
echo "$VAR"
更好的是,您可以使用缩进使其在您的代码中更加突出。这次只需在<<
之后添加-
即可阻止标签出现。
read -r -d '' VAR <<- EOM
This is line 1.
This is line 2.
Line 3.
EOM
但是你必须在代码中使用制表符而不是空格来缩进。
【讨论】:
为什么在第 2 和第 5 个代码块的第一行有 @sup3rman '-' 忽略前导制表符。见***.com/questions/2500436/… 如何让状态为 0 的读取退出?似乎找不到行尾(因为它是 -d '')并以 1 退出,这在您使用脚本时无济于事。 @MishaSlyusarev 使用 -d '\0' 并在最后一行的末尾添加一个 \0 在read -r -d '' VARIABLE <<- EOM
中使用-d
选项对我不起作用。【参考方案6】:
只是提到一个简单的单行连接,因为它有时很有用。
# for bash
v=" guga "$'\n'" puga "
# Just for an example.
v2="bar "$'\n'" foo "$'\n'"$v"
# Let's simplify the previous version of $v2.
n=$'\n'
v3="bar $n foo $n$v"
echo "$v3"
你会得到这样的东西
酒吧 富 古加 普加所有开头和结尾的空格都将保留
echo "$v3" > filename
【讨论】:
【参考方案7】:在 bash 脚本中,以下工作:
#!/bin/sh
text="this is line one\nthis is line two\nthis is line three"
echo -e $text > filename
或者:
text="this is line one
this is line two
this is line three"
echo "$text" > filename
cat 文件名给出:
this is line one
this is line two
this is line three
【讨论】:
似乎替代技巧在 Shell 脚本中有效,但似乎不适用于 bash。 @Macindows 我似乎忘记了echo
的-e
选项。请重试。【参考方案8】:
我找到了更多解决方案,因为我想让每一行都正确缩进:
你可以使用echo
:
echo "this is line one" \
"\n""this is line two" \
"\n""this is line three" \
> filename
如果您将"\n"
放在行尾的\
之前,则不起作用。
或者,您可以使用printf
以获得更好的可移植性(我碰巧使用echo
有很多问题):
printf '%s\n' \
"this is line one" \
"this is line two" \
"this is line three" \
> filename
另一种解决方案可能是:
text=''
text="$textthis is line one\n"
text="$textthis is line two\n"
text="$textthis is line three\n"
printf "%b" "$text" > filename
或
text=''
text+="this is line one\n"
text+="this is line two\n"
text+="this is line three\n"
printf "%b" "$text" > filename
另一种解决方案是通过混合printf
和sed
来实现。
if something
then
printf '%s' '
this is line one
this is line two
this is line three
' | sed '1d;$d;s/^ //g'
fi
当您将缩进级别硬编码到代码中时,重构这样格式化的代码并不容易。
可以使用辅助函数和一些变量替换技巧:
unset text
_() text="$text$text+
$*";
# That's an empty line which demonstrates the reasoning behind
# the usage of "+" instead of ":+" in the variable substitution
# above.
_ ""
_ "this is line one"
_ "this is line two"
_ "this is line three"
unset -f _
printf '%s' "$text"
【讨论】:
3b 是我想要独立保留代码缩进和字符串缩进的首选解决方案,至少当我不能在变量赋值中使用 2 并且比 3a 更具可读性时。当我不在乎时,我只是在几行中保持带引号的字符串打开。 非常好的答案,尤其是 3b "如果你在行尾的 \ 前面加上 "\n" 是行不通的。" - 在使用 echo 时是一个很好的提示。 printf 将我保存在一个简单的 docker 容器中,不需要 \n 真的很好【参考方案9】:如果你把它放在下面,它会起作用:
AA='first line
\nsecond line
\nthird line'
echo $AA
output:
first line
second line
third line
【讨论】:
【参考方案10】:以下是我将多行字符串分配给变量的首选方式(我认为它看起来不错)。
read -r -d '' my_variable << \
_______________________________________________________________________________
String1
String2
String3
...
StringN
_______________________________________________________________________________
两种情况下下划线的数量相同(此处为 80)。
【讨论】:
这很漂亮。谢谢!【参考方案11】:如果您试图将字符串放入变量中,另一种简单的方法是这样的:
USAGE=$(cat <<-END
This is line one.
This is line two.
This is line three.
END
)
如果你用制表符缩进你的字符串(即'\t'),缩进将被去除。如果你用空格缩进,缩进会留在里面。
注意: 重要的是,最后一个右括号位于另一行。 END
文本必须单独出现在一行中。
【讨论】:
这很重要吗?在我的 Mac 上工作,)
在同一行。我认为这是因为$(
和)
之间的内容将在它自己的宇宙中执行,而实际的命令无论如何都不会看到')'。我想知道它是否也适用于其他人。
不同的shell可能会以不同的方式解释事物。在bash documentation 中,它似乎并没有以一种或另一种方式说明任何事情,但所有的例子都有它自己的一行。
有趣的是,我发现你在尝试为 USAGE 变量设置值时也回答了。所以你的答案完全匹配。 :)
@deej 来自POSIX spec:here-document … 一直持续到有一行只包含分隔符和 echo
在传递给它的参数之间添加空格。 $text
是可变扩展和分词的,所以你的echo
命令相当于:
echo -e "this" "is" "line" "one\n" "this" "is" "line" "two\n" ...
可以看到“this”前面会加一个空格。您可以删除换行符,并引用 $text
以保留换行符:
text="this is line one
this is line two
this is line three"
echo "$text" > filename
或者你可以使用printf
,它比echo
更健壮和便携:
printf "%s\n" "this is line one" "this is line two" "this is line three" > filename
在支持大括号扩展的bash
中,您甚至可以这样做:
printf "%s\n" "this is line "one,two,three > filename
【讨论】:
感谢您解释为什么 mod 在使用“echo”命令时会看到额外的空间。在 bash 脚本中使用时,这个答案也很有效(与交互式 shell 会话相反)。感觉这是真正的答案,应该是公认的答案。 这应该是被接受的答案。所有其他答案都提供了大量有趣的 other 方法来解决 OP 的痛苦,但从技术上讲,问题是“额外的空间是如何产生的”,没有人解决这个问题 :-) !!!谢谢你,乔希让 -1 变得神秘!以上是关于带有额外空格的多行字符串(保留缩进)的主要内容,如果未能解决你的问题,请参考以下文章