如何在 bash here-document 中键入选项卡?

Posted

技术标签:

【中文标题】如何在 bash here-document 中键入选项卡?【英文标题】:How do you type a tab in a bash here-document? 【发布时间】:2011-04-13 11:45:24 【问题描述】:

here-document 的定义在这里: http://en.wikipedia.org/wiki/Here_document

如何在此处文档中键入选项卡?比如这样:

cat > prices.txt << EOF
coffee\t$1.50
tea\t$1.50
burger\t$5.00
EOF

更新:

本题涉及的问题:

    扩展制表符字符 虽然没有展开 美元符号 在脚本等文件中嵌入 here-doc

【问题讨论】:

这个网站也很有意思:tldp.org/LDP/abs/html/abs-guide.html#GENERATESCRIPT 【参考方案1】:
TAB="$(printf '\t')"

cat > prices.txt << EOF
coffee$TAB\$1.50
tea$TAB\$1.50
burger$TAB\$5.00
EOF

【讨论】:

+1 感谢您提到在此处文档中包含标签的简单解决方案。 bash 特定的快捷方式是 TAB=$'\t',滥用 gettext 功能。 @Daenyth,感谢您的提示,这是一种更优雅的方式。 我更喜欢$t,但这太棒了。我认为当它只是需要的选项卡时,这是这里提出的最佳解决方案。 (fwiw-printf 是更便携的选项)【参考方案2】:

您可以将您的 here doc 嵌入到您的脚本中,并将其分配给一个变量,而无需使用单独的文件:

#!/bin/bash
read -r -d '' var<<"EOF"
coffee\t$1.50
tea\t$1.50
burger\t$5.00
EOF

然后printfecho -e 会将\t 字符扩展为制表符。您可以将其输出到文件中:

printf "%s\n" "$var" > prices.txt

或者使用printf -v将变量的值赋值给自身:

printf -v var "%s\n" "$var"

现在var 或文件prices.txt 包含实际的标签而不是\t

您可以在读取此处的文档时对其进行处理,而不是将其存储在变量中或将其写入文件:

while read -r item price
do
    printf "The price of %s is %s.\n" $item $price    # as a sentence
    printf "%s\t%s\n" $item $price                  # as a tab-delimited line
done <<- "EOF"
    coffee $1.50    # I'm using spaces between fields in this case
    tea $1.50
    burger $5.00
    EOF

请注意,在这种情况下,我将 &lt;&lt;- 用于此处的 doc 运算符。这使我可以缩进此处文档的行以提高可读性。缩进必须仅包含制表符(无空格)。

【讨论】:

+1 我从这个答案中学到了最多的东西,它解决了我在 here-docs 中遇到的所有问题,以及一些有效使用它们的方法。 对于 bash v4.4.19,printf 必须使用 %b 格式说明符而不是 %s 来扩展转义制表符 (\t)。见unix.stackexchange.com/a/454069/29426。【参考方案3】:

对于我来说,我键入 ctrl-V 后跟 ctrl-I 以在 bash shell 中插入一个选项卡。这绕过了拦截选项卡的外壳,否则它具有“特殊”含义。 Ctrl-V 后跟一个选项卡也应该可以工作。

在此处文档中嵌入美元符号时,您需要禁用 shell 变量的插值,或者在每个变量前加上反斜杠以进行转义(即 \$)。

使用您的示例文本,我最终在 price.txt 中找到了此内容:

coffee\t.50
tea\t.50
burger\t.00

因为 $1$5 没有设置。可以通过引用终止符来关闭插值,例如:

cat > prices.txt <<"EOF"

【讨论】:

+1 感谢您的回答。如果我想将此处的文档存储在文件中以供以后运行怎么办?例如,如果我想使用 here-doc 来提供一个示例文件,我可以在运行特定脚本时将注释过的 here-doc 存储在与脚本相同的文件中以供以后参考? @D W - 我不确定我是否遵循你的要求 - 你当然可以在脚本中嵌入文档。 感谢您提及在此处文档中包含美元符号而不对其进行解释的解决方案。【参考方案4】:

正如其他人所说,您可以在键入时键入 CTRL-V 然后 tab 以插入单个选项卡。

您也可以暂时禁用 bash 制表符补全功能,例如,如果您想粘贴文本,或者如果您想输入一个带有很多制表符的长 here-doc:

bind '\C-i:self-insert'     # disable tab-completion

# paste some text or type your here-doc
# note you don't use "\t" you just press tab

bind '\C-i:complete'        # reenable tab-completion

编辑:如果您在 Mac 上使用 iTerm 2,现在有一个“使用文字标签粘贴”选项,允许将带有标签的代码粘贴到 bash 命令行。

【讨论】:

【参考方案5】:

我注意到已经给出了正确答案,但我正在尝试总结为更简洁的答案。

1. 没有什么可以阻止您在此处的文档中使用文字制表符。

要在 Bash 提示符下键入文字选项卡,您需要对其进行转义。转义字符是 ctrl-V(除非您有自定义绑定来覆盖它)。

$ echo -n 't<ctrl-v><tab>ab' | hexdump -C
00000000  74 09 61 62                                       |t.ab|
00000004

在大多数程序员的编辑器中,插入文字制表符应该是微不足道的(尽管有些编辑器也可能想要转义。在 Emacs 中,ctrl-Q TAB 插入文字制表符)。

为了便于阅读,最好使用某种转义符而不是文字制表符。在 Bash 中,$'...' 字符串语法很方便。

2. 为防止变量扩展,请引用所有美元符号,或将 here doc 终止符放在引号中。

$ hexdump -C <<HERE
> t<ctrl-v><tab>\$ab
HERE
00000000  74 09 24 61 62 0a                                 |t.$ab.|
00000006

$ hexdump -C <<'HERE'
> t<ctrl-v><tab>$ab
HERE
00000000  74 09 24 61 62 0a                                 |t.$ab.|
00000006

在这种情况下,使用单引号还是双引号都没有关系。

3. 我不确定我是否理解这个子问题。此处文档的目的是将其嵌入到脚本中。前面的示例说明了如何在脚本或命令行中将 here 文档传递给 hexdump。

如果您想多次使用同一个此处文档,则没有直接的方法可以直接执行此操作。该脚本可以将此处的文档写入临时文件,然后将该临时文件传递给多个命令,然后擦除该临时文件。 (注意使用trap 删除临时文件,以防脚本中断。)

您也可以将此处文档的内容放入一个变量中,然后对其进行插值。

# Note embedded newlines inside the single quotes,
# and the use of $'...\t...' to encode tabs
data=$'coffee\t$1.50
tea\t$1.50
burger\t$5.00'

# Run Word Count on the data using a here document
wc <<HERE
$data
HERE

# Count number of tab characters using another here document with the same data
grep -c $'\t' <<HERE
$data
HERE

您可以等效地使用echo -E "$data" | wc; echo -E "$data" | grep -c $'\t',但使用echo 不是很优雅,并且可能不太便携(尽管如果您的目标是bash,所有回声应该是相同的。如果您的目标是一般的Bourne shell,您可能还会花费每个回显都有一个外部进程)。

【讨论】:

【参考方案6】:

这是Dennis Williamson's answer 的简短版本。灵感来自这里:http://tldp.org/LDP/abs/html/here-docs.html.

#!/bin/bash

var=$(echo -e "$(cat <<"EOF"
coffee\t$1.50
tea\t$1.50
burger\t$5.00
EOF
)")

echo "$var"

【讨论】:

【参考方案7】:

粘贴到heredoc 中的选项卡消失了,因为bash 仍将它们解释为标记自动完成序列/请求开始的特殊字符。

如果您想在当前 shell 中快速粘贴 heredoc,可以通过在当前 shell 的生命周期内禁用自动完成来实现。

如果您粘贴包含标签的heredoc,正常的自动完成会发生以下情况:

$ cat <<EOF
TABAFTERTABBEFORE
TABAFTERTABBEFORE
TABAFTERTABBEFORE
TABAFTERTABBEFORE
TABAFTERTABBEFORE
EOF
TABAFTERTABBEFORE
TABAFTERTABBEFORE
TABAFTERTABBEFORE
TABAFTERTABBEFORE
TABAFTERTABBEFORE

运行这个命令:

bind "set disable-completion on"

再次粘贴并保留您的标签:

$ cat <<EOF
> TABAFTER      TABBEFORE
> TABAFTER      TABBEFORE
> TABAFTER      TABBEFORE
> TABAFTER      TABBEFORE
> TABAFTER      TABBEFORE
> EOF
TABAFTER    TABBEFORE
TABAFTER    TABBEFORE
TABAFTER    TABBEFORE
TABAFTER    TABBEFORE
TABAFTER    TABBEFORE

【讨论】:

如果复制带有包含制表符的格式化块的heredoc,这非常方便。关闭扩展bind "set disable-completion off",粘贴带有标签的heredoc,重新打开bind "set disable-completion on"【参考方案8】:

Re:子问题 #3,我将这个问题读作:

"[W]如果我想[...] 存储此处注释的文档 文件作为脚本供以后参考?”

使用脚本名称作为此处文档的输出,追加而不是替换,假设执行者也有写权限。在 here doc 块期间不会解释 Shell cmets。

_thisline=$LINENO
cat <<EOF >>$0
#====== $(date) =========
#On this run, these variable values were used as of line $_thisline: A=$A, B=$B, B=$C

EOF

以类似的方式,您可以使用 here doc 编写一个将变量扩展为值的新脚本,执行它,然后您就可以准确记录运行的内容,而不必跟踪代码。

【讨论】:

【参考方案9】:

原始问题的一个简单直接的解决方案是使用 $(echo $'...') 成语:

cat > prices.txt << EOF
$(echo $'coffee\t$1.50')
$(echo $'tea\t$1.50')
$(echo $'burger\t$5.00')
EOF

【讨论】:

【参考方案10】:

如果您使用 sed 之类的工具并引用分隔符 (EOF),事情会变得更简单:

sed 's/\\t/\t/g' > prices.txt << 'EOF'
coffee\t$1.50
tea\t$1.50
burger\t$5.00
EOF
引用 EOF 可防止参数(美元符号)扩展。 sed 将 '\t' 转换为制表符。 如果您在此处的文档中有 \\t 之类的模式,那么您将需要更复杂的 sed 调用,例如:sed -e 's/\\t/\t/g' -e 's/\\\t/\\t/g'

【讨论】:

【参考方案11】:

如果你想为文件缩进使用制表符为heredoc: 您只需要使用空格将缩进的选项卡与文档的选项卡分开:

try_me() 
    # @LinuxGuru's snippet; thanks!
    sed 's/^ //g' >> tmp.conf <<-EOF
     /var/log/nginx/*log  
        daily
        rotate 10
        missingok
        notifempty
        compress
        sharedscripts
        postrotate
            /bin/kill -USR1 $(cat /var/run/nginx.pid 2>/dev/null) 2>/dev/null || :
        endscript
     
    EOF


try_me

唯一的缺点是不缩进的行看起来有点奇怪;他们在脚本上有一个前导空格字符

/var/log/nginx/*log

但是,生成的文件中不会出现这种情况(sed 's/^ //g' 而不是 cat

【讨论】:

【参考方案12】:

使用@EOF,它会保留标签。

cat >> /etc/logrotate.d/nginx <<@EOF
/var/log/nginx/*log  
    daily
    rotate 10
    missingok
    notifempty
    compress
    sharedscripts
    postrotate
        /bin/kill -USR1 $(cat /var/run/nginx.pid 2>/dev/null) 2>/dev/null || :
    endscript

@EOF

【讨论】:

你使用的是什么版本的 BASH? @LinuxGuru: ``` $ bash --version GNU bash,版本 5.0.3(1)-release (x86_64-pc-linux-gnu) ```

以上是关于如何在 bash here-document 中键入选项卡?的主要内容,如果未能解决你的问题,请参考以下文章

Linux使用命令CD时候使用tab报-bash: cannot create temp file for here-document: No space left on

Linux使用命令CD时候使用tab报-bash: cannot create temp file for here-document: No space left on

Linux出现cannot create temp file for here-document: No space left on device的问题解决

Linux出现cannot create temp file for here-document: No space left on device的问题解决

R 是啥时候开始支持类似 here-document 的功能的? [关闭]

here-document 给出“文件意外结束”错误