我在 Bash 中执行以下代码:
bash /dev/fd/10 10<<-'SES'
cat <<EMR >> /etc/apache2/apache2.conf
#
<Directory /var/www/>
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>
EMR
SES
但执行中断了,我得到:
警告:此处文档第 2 行由文件结尾分隔(需要“EMR”)
- 我不明白该错误的含义,并且我已经
-
在运算符和包含的定界文档的名称之间添加了以允许制表。 - 我确定没有空格后这开场白(运算符+名称)或之后分隔符。
注意:如果您复制上面的代码在计算机上进行测试,则需要将所有前导空格更改为制表符(制表符),如下所示堆栈交换将原来的表格改为空格。
可能需要排除的原因:
造成这种情况的可能原因有多种,以下是我排除的一些原因:
1.有问题的隐藏字符:
我使用 Notepad++ 编辑文件,并打开“显示所有符号”模式,以确保所有缩进都是基于表格的(参见红色箭头),并且所有行尾字符 (EOL) 都是LF
基于 UNIX 的(没有任何CR
字符) ):
而且编码是UTF-8
,所以不是编码问题。
2. 伪破折号(<<-
):
似乎添加的连字符 ( <<-
) 没有完成剥离所有前导选项卡的工作,因为当我手动删除所有前导选项卡时任何(或所有主要选项卡每个分隔符之前)heredoc 确实按预期工作。人们可以建议连字符不是真的短跑符号,但为什么不是呢?我没有办法验证这一点。
3. 重击错误:
其他不使用 Windows 以及使用不同的包含 Bash 的 Linux 发行版的人没有这个问题,因此这很可能不是 Bash 错误。我还在 GNU 的 Bash 开发部分对此进行了测试。
从 Notepad++ 复制的数据粘贴损坏可能是:
如果我将 Notepad++ 中的heredoc粘贴到nano
文件中,似乎前导选项卡都已就位 - 它们没有被删除,而是在粘贴时转换为空格。
此外,cat script.sh | grep "^ "
并且cat script.sh | grep -x '\s*EMR'
(在文件目录中完成时),结果为空。
然而,在后来的粘贴中,我发现了另一个最有可能导致此问题的腐败问题(请参阅我的答案)。
答案1
您发布的图像显示回车符(CR
在图像中)。这是 DOS 格式的文本文件。使用dos2unix
或一些类似的实用程序将文件转换为正确的 Unix 文本文件。
根据下面的评论:如果您绝对需要在 Windows 上的 Notepad++ 中进行开发(我个人建议在您所针对的同一操作系统上进行开发,如果可能的话),那么程序可能会被告知保存文本文件通过在“新文档”首选项中选择“Unix (LF)”来使用 Unix 换行符:
确保文件的LF
最后一行末尾有换行符 ( )。 全部Unix 文本文件中的行(shell 脚本是文本文件)需要末尾有一个终止换行符。如果最后一行没有,从技术上讲,该文件实际上不是文本文件,而是二进制文件。
例如,另请参阅“从日志文件中删除 ^M 字符”
答案2
不幸的是,终端模拟器(例如 Ubuntu 盒子上的终端窗口)的设计大多不允许良好的粘贴支持;特别是,在大多数情况下,粘贴的工作方式与键入相同的内容完全相同(如果您是非常快速打字员)。大多数你认为是终端中运行的程序的特殊信号的东西实际上是带内数据——也就是说,只是一个字符。如果您的粘贴包含该字符,则程序无法区分您键入该字符和粘贴它之间的区别。
因此,您不能真正使用粘贴来传输文件,除非它们不包含可能混淆接收程序的字符。特别是,您粘贴的选项卡会让 bash 感到困惑——它看到一个选项卡并尝试完成文件或命令完成。就像您键入制表符时一样。
cat > filename
如果粘贴的内容中有一个control-D(是的,这是一个字符),这个技巧很可能会失败。但如果您避免控制字符(换行符和制表符除外),它应该可以工作。
Unix 用户通常使用文件传输程序来移动文件。我们最常用的基本方法是scp
或sftp
,这两者都是 SSH 的一部分(因此完全加密、经过身份验证等 — 与 SSH 提供的安全保证相同)。
在 Windows 和 Ubuntu VM 之间工作(反之亦然)的最简单方法可能是使用虚拟机管理程序(VM 产品)的文件共享功能 — 大多数都具有这些功能。您可以在主机上选择一个目录,它将在来宾中可见。使用 WSL,您可以访问/mnt/<drive letter>/
.如果您的虚拟机管理程序没有该功能,第二简单的方法是使用普通文件共享。您可以从 Ubuntu 挂载 Windows 文件共享,或在 Ubuntu 上安装 Samba 以将文件共享到 Windows。
根据您正在做的事情,可能有更好的方法。例如,自动化部署。也许您希望将文件签入 git,然后当您执行git push
?操作时,它会自动部署到您的 Ubuntu 机器上。这可以像使用 git hooks 一样简单,也可以复杂得多——有专门用于它的完整软件包。
最后,将这些脚本写入文件(而不仅仅是将它们粘贴到 shell 中)有一个很大的优点:该文件可以作为您所做操作的文档。出了什么问题吗?除非它去了真的错误,检查文件看看原因。这可以让您准确地确定出了什么问题,这通常是修复损坏的第一步。需要再做一次吗?只需再次获取该文件即可。需要做类似的事情吗?编辑该文件,然后获取它。
如果你必须粘贴此处文档:
如果您想直接粘贴到 bash,最好的办法是确保它不包含除换行符之外的控制字符。粘贴这个应该可以:
bash /dev/fd/10 10<<'SES'
cat <<EMR >> /etc/apache2/apache2.conf
#
<Directory /var/www/>
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>
EMR
SES
如果必须有缩进,可以使用空格和 sed 而不是前导-
来删除它(请注意,这里继续使用 stdin 而不是 fd 10,因为语法更简单,我怀疑它很重要):
sed -e 's/^\s\+//' <<'SES' | bash
cat <<EMR >> /etc/apache2/apache2.conf
#
<Directory /var/www/>
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>
EMR
SES
它使用 sed 的搜索替换功能 ( s/PATTERN/REPLACEMENT/
) 在行的开头 ( ) 中查找重复一次或多次 ( )^
的空格 ( ) 并将其替换为空。换句话说,它会修剪掉每行上的所有前导空格。 sed 的选项明确标识这是一个要运行的 sed 脚本 - 实际上并不是必需的,sed 无论如何都会将其第一个参数解释为脚本 - 但我个人发现始终使用.\s
\+
-e
-e
请注意,上面的“空白”包括空格和制表符(以及一些与此处并不真正相关的字符)。无论使用何种组合,sed 都会将它们全部修剪掉。
当然,我不知道为什么你要将命令粘贴到 bash 中,告诉它运行 bash 来运行 cat。
答案3
问题毫无疑问源自Windows。它处理 TTY 窗口大小;窗口尺寸越窄,换行( LF
) 字符和回车( CR
) 字符越多连击将根据窗口的狭窄程度添加到粘贴中。此类组合表示为CRLF
。
解决方案
如果我了解 Nano 的维护者本诺·舒伦伯格正确的,Nano 会自然地将CRLF
组合转换为尾随空白字符(^J
chars )。
正如 Benno 向我建议的那样,在最后添加以下代码/etc/nanorc
解决了这个问题:
bind ^J enter main
澄清解决方案
一方面,它将禁止形成^J
破坏代码的字符;另一方面,它会添加仅有的换行符(LF
字符)适用于 Linux(与 WindowsCRLF
组合不同),用于复制的数据。
单独添加的LF
字符很好,因为它们可以防止粘贴的数据显示为一长行文本。
- 我也向WSL开发团队. WSL 是Linux 的 Windows 子系统项目。