如何阻止vim在文件末尾添加新行?

342

我在PHP团队工作,我们都使用不同的编辑器,并且必须在Windows上工作。我使用vim,但是每当我编辑一个文件时,所有人都抱怨底部会有一个换行符。我找到了相关资料,发现这是vi和vim的已知行为...但我想知道是否有方法可以禁用此功能(最好是只针对特定的文件扩展名)。

如果有人知道如何做到这一点,那就太好了!


41
你应该告诉他们他们的想法有些傻 - vim这样做实际上是有很好的原因的:https://dev59.com/b3RB5IYBdhLWcg3wET1J。我猜只有在部署到类Unix服务器时才相关。 - hdgarrood
7
官方PHP推荐的做法是省略最后一个?>闭合标签,就是出于这个原因。 - Sebastián Grignoli
这个问题可能早于超级用户网站的存在...但应该在那里。 - gcb
35
问题实际上是:我们如何告诉所有其他编辑器,在你的工作室中使用的人们,确保文件的最后一行确实以EOL结尾。;-) - T.J. Crowder
14个回答

503
对于vim 7.4+,你可以使用以下任意一种方法(最好在你的.vimrc文件中)(感谢罗泽轩提供的最新消息!):
:set nofixeol
:set nofixendofline

关于旧版本的vim。
即使文件已经保存了末尾的换行符:
在vim中执行以下命令: vim -b file 然后进入vim编辑器。
:set noeol
:wq

完成。

或者你可以用:e ++bin file在vim中打开文件。

还有另一种选择:

:set binary
:set noeol
:wq

请查看更多详细信息:为什么我需要在二进制模式下使用vim才能使'noeol'起作用?


8
您可以在您的 .vimrc 文件中添加 "set binary" 和 "set noeol" 来使其更加通用且易于理解。请注意,翻译内容不包括解释或其他内容。 - Rey0bs
22
注意:binary 会覆盖 expandtab,这将导致源代码中出现制表符而非空格。 - Ciro Santilli OurBigBook.com
5
谢谢!我一直在寻找二进制的副作用,也一直在想为什么它不是默认设置!因为我喜欢我的标签页,所以我认为我会考虑这个额外的好处 :) - gcb
13
你可以在Vim 7.4+中加入set nofixendofline来解决这个问题。 - 罗泽轩
1
@TrevorBoydSmith 是的,因为历史上 POSIX(我可能对实际标准有所错误)期望在文本文件的行末添加换行符。这就是为什么大多数解决方案需要将其视为二进制文件的原因。那时它可能非常方便,但今天已经过时了,这就是为什么现在有了方便的设置。只需将其添加到您的 vimrc 中即可。无论如何,在其他编辑器中存在更糟糕的问题 :) - gcb
显示剩余5条评论

24

将以下命令添加到您的.vimrc中以关闭行尾选项:

autocmd FileType php setlocal noeol binary fileformat=dos

然而,PHP本身会忽略最后的换行符 - 这不应该是一个问题。我几乎可以确定,在您的情况下,还有其他一些东西正在添加最后的换行符,或者可能存在Windows / Unix行尾类型(\n\r\n等)的混淆。

更新:

另一种解决方法可能只需将此行添加到您的 .vimrc 中:

set fileformats+=dos

我知道 PHP 并没有解析错误... 我已经向我的同事展示了,但 WinMerge 认为它们是不同的... 我会尝试一下并告诉你结果如何。 - Boushley
这样做的效果是没有最后一行,但它也会将文件转换为Unix行尾(即使在Windows上)...所以我之前通过切换到二进制模式来解决这个问题...但是在记事本中,行尾不会正确显示...所有内容都在一行中。 - Boushley
不幸的是,这些建议都没有起作用。 将fileformat=dos添加到autocmd中没有任何效果,而set filetype+=dos仍然会添加拖尾\n。 - Boushley
抱歉,我弄错了,应该使用“set filetypes+=dos”,而不是“set fileformats...”。 - too much php

20

如果你正在使用 Git 进行源代码管理,还有另一种方法可以解决这个问题。灵感来自这里的一个答案,我编写了自己的过滤器,用于在gitattributes文件中使用。

要安装此过滤器,请将其保存为 noeol_filter 并放置在您的 $PATH 中某个地方,然后赋予其可执行权限,并运行以下命令:

git config --global filter.noeol.clean noeol_filter
git config --global filter.noeol.smudge cat

要仅为自己启用筛选器,请将以下行放入$GIT_DIR/info/attributes中:

*.php filter=noeol

这将确保您不会在 .php 文件的 eof 处提交任何换行符,无论 Vim 如何操作。

现在,让我们看一下脚本本身:

#!/usr/bin/python

# a filter that strips newline from last line of its stdin
# if the last line is empty, leave it as-is, to make the operation idempotent
# inspired by: https://dev59.com/CHI-5IYBdhLWcg3w0cOG#1663283

import sys

if __name__ == '__main__':
    try:
        pline = sys.stdin.next()
    except StopIteration:
        # no input, nothing to do
        sys.exit(0)

    # spit out all but the last line
    for line in sys.stdin:
        sys.stdout.write(pline)
        pline = line

    # strip newline from last line before spitting it out
    if len(pline) > 2 and pline.endswith("\r\n"):
        sys.stdout.write(pline[:-2])
    elif len(pline) > 1 and pline.endswith("\n"):
        sys.stdout.write(pline[:-1])
    else:
        sys.stdout.write(pline)

这是一个很好的解决方案...尽管不幸的是,我当时并没有使用git。我正在使用svn,但我认为可以采用类似的方法。总之,我已经离开了那个位置,不再需要担心这个了 :) - Boushley

13

我没有尝试过这个选项,但 vim 帮助系统(即 help eol)中提供了以下信息:

'endofline' 'eol'   boolean (default on)
            local to buffer
            {not in Vi}

When writing a file and this option is off and the 'binary' option
is on, no <EOL> will be written for the last line in the file.  This
option is automatically set when starting to edit a new file, unless
the file does not have an <EOL> for the last line in the file, in
which case it is reset.  

通常情况下,您不需要设置或重置此选项。当“binary”处于关闭状态时,写入文件时不使用该值。当“binary”处于打开状态时,用于记住文件中最后一行的“\n”符号是否存在,以便在写入文件时可以保持与原始文件相同的情况。但是如果您想要更改它,也是可以的。

您可能对之前的一个问题的答案感兴趣:“为什么文件应该以换行符结尾”。


我也遇到了 eol 设置......但它不能完成这个任务。 它更像是一个变量,用于确定是否已经将其放置在文件末尾。 而且,它对文本文件没有影响,只有对二进制文件有效。 - Boushley
3
关于 eol,Vim 帮助文档也指出:"如果关闭 'binary' 选项,在写入文件时不会使用该值。" - Boushley
1
VonC在那个问题的回答中得到了+1,他强调了“newline”和EOL被混淆的事实。由于EOL,低劣的编辑器错误地显示额外的换行符,而vim并没有添加“newline”! - ches

10

5
Vim 7.4.785增加了fixeol选项,可以禁用自动保留文件末尾缺失EOL的功能。对于Vim 7.4.785及更高版本,此脚本变得不必要。谢谢,我不知道有这个新选项。 - Amir
4
我必须同时设置 noeolnofixeol 才能达到理想的结果。 - selurvedu

8

好的,你使用Windows会让事情变得复杂 ;)

由于“binary”选项会重置“fileformat”选项(并且设置了“binary”后始终使用Unix换行符进行写入),所以让我们拿出大锤子并在外部进行操作!

如何为BufWritePost事件定义一个自动命令(:help autocommand)?每次写入整个缓冲区后都会执行此自动命令。在此自动命令中调用一个小型外部工具(php、perl或其他脚本),以去除刚刚写入文件的最后一个换行符。

因此,这将类似于以下内容,并将放入您的.vimrc文件中:

autocmd!   "Remove all autocmds (for current group), see below"
autocmd BufWritePost *.php !your-script <afile>

如果这是您第一次处理autocommands,请务必阅读有关vim文档的全部内容。有一些注意事项,例如建议在您的.vimrc中删除所有autocmds,以防您的.vimrc可能被多次引用。


嗯...我希望有比这更好的解决方案...但这绝对有效! - Boushley
3
这很好用!太棒了!但是我有一个问题...有没有办法让我在每次保存后不必按两次回车。我需要在弹出的cmd窗口中按回车,然后再次按回车,因为vim告诉我脚本成功返回了... 有没有办法让它们都消失? - Boushley
2
为了避免出现 <Press Enter> 的提示,只需在命令前加上“silent”。例如:silent !your-script <afile> - dash-tom-bang

6

6

我已经采用了Blixtor的建议,使用Perl和Python进行后处理,可以在Vim中运行(如果编译时支持这种语言),也可以通过外部Perl脚本运行。它作为PreserveNoEOL插件在vim.org上提供。


我还没有彻底测试过,但那似乎是一个更好的解决方案。 - Boushley
插件可以使用,但是在安装后我不得不将 let g:PreserveNoEOL = 1 放入我的 .vimrc 文件中!从插件描述中了解到这一点需要学习 vimscript! :D - DarthVanger
1
@DarthVanger::help PreserveNoEOL-usage 也会告诉你的。RTFM :-) - Ingo Karkat
@IngoKarkat 对不起,我一直在INSTALLATIONCONFIGURATION下寻找它。因为它在安装说明之上,所以甚至没有注意到描述中的USAGE部分 :) - DarthVanger

3
也许你可以看看他们为什么在抱怨。如果一个php文件在结尾的?>之后有一个换行符,php将其作为页面的一部分输出。这不是问题,除非你尝试在文件被包含后发送标头。
但是,php文件结尾处的?>是可选的。没有?> 结尾,文件末尾的换行符就不会有问题。

2
你说得没错,但我们作为一个团队决定保留结尾的 ?> 标记,因为这样看起来更好。我知道我们可以不用它... 但我更愿意调整我的编辑器以适应我的编码风格,而不是反过来。 - Boushley
1
另外,你需要知道的是,PHP会自动将你的结束标签解析为“?>”或“?>\n”(因为在Linux中,所有有效文件都应该以“\n”结尾)... 所以我的代码不会引起这些问题... 它们只是不喜欢它的样子。 - Boushley

2

尝试在.vimrc中添加以下内容:

set binary

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接