如何在 Vim 或 Linux 中将空格转换为制表符?
Posted
技术标签:
【中文标题】如何在 Vim 或 Linux 中将空格转换为制表符?【英文标题】:How can I convert spaces to tabs in Vim or Linux? 【发布时间】:2012-02-24 15:46:48 【问题描述】:我查看了 Stack Overflow 上的几个问题,了解如何将空格转换为制表符而不找到我需要的内容。似乎有更多关于如何将制表符转换为空格的问题,但我正在尝试做相反的事情。
在Vim
中,我尝试过:retab
和:retab!
,但没有运气,但我相信这些实际上是为了从制表符到空格。
我在命令提示符下尝试了expand
和unexpand
,但没有任何运气。
这是有问题的文件:
http://gdata-python-client.googlecode.com/hg-history/a9ed9edefd61a0ba0e18c43e448472051821003a/samples/docs/docs_v3_example.py
如何使用 Vim
或 shell 将 前导 空格转换为制表符?
【问题讨论】:
在@Matt 的现在已删除的评论中,第一个示例 (sed "s/ +/`echo -e '\t'`/g" < input.py > output.py
) 似乎转换了所有空格,而不仅仅是前导空格。在第二个示例 (sed "s/^ +/`echo -e '\t'`/g" < input.py > output.py
) 中,它仅将每行的第一个空格替换为一个制表符,并保留其余的空格。
对面相关:How to replace tabs with spaces? at Vim SE
我没有找到所有/许多文件的答案,所以我自己写了一个:***.com/a/44581474/1115187。使用find
、awk
和二十一点(不过,太长了,无法将其留在 cmets 中)
这能回答你的问题吗? How can I convert tabs to spaces in every file of a directory?
【参考方案1】:
在带有 GNU coreutils 的 shell 中:
unexpand -t2 --first-only originalFile > newFile
解释:
unexpand
: 将空格变成制表符的命令
-t2
:将 2 个空格变成 1 个制表符
--first-only
:只修改行首的空格(包括混合制表符和空格),不修改行中或行尾的空格
它的倒数是expand
,它需要一个简单的-i
而不是--first-only
,-t2
不变
Johnsyweb 流行的绿色检查答案完全是错误的,因为它修改了所有地方的空格!!!
附言这篇文章的疯狂格式并不能反映内容的质量。它反映了 *** 如何不支持我的浏览器。谢谢。
【讨论】:
【参考方案2】:将所有空格更改为制表符
:%s/\s/\t/g
【讨论】:
这正是我所需要的。谢谢。我必须做的唯一改变是用制表符替换每 4 个空格,而不是每一个空格。 这会将所有空格转换为制表符,而不仅仅是缩进。【参考方案3】:要使用 Vim 将一组文件(例如目录层次结构中的所有 *.ts 文件)从 2 个空格变为 4 个空格,您可以从命令行尝试:
find . -name '*.ts' -print0 | xargs -0 -n1 vim -e '+set ts=2 noet | retab! | set ts=4 et | retab | wq'
这样做是使用find
将所有匹配的文件传递给xargs
(find 上的 -print0 选项与 xargs 的 -0 选项一起使用,以便处理名称中带有空格的文件)。
xargs 在每个文件上以 ex 模式 (-e
) 运行 vim,执行给定的 ex 命令实际上是几个命令,将现有的前导空格更改为制表符,重置制表位并将制表符更改回空格,最后保存并退出。
在 ex 模式下运行可以防止这种情况:Vim: Warning: Input is not from a terminal
用于每个文件。
【讨论】:
【参考方案4】:Linux:使用unexpand
(和expand
)
这是一个非常好的解决方案:https://***.com/a/11094620/1115187,主要是因为它使用了 *nix-utilities:
-
unexpand — 空格 -> 制表符
expand — 制表符 -> 空格
Linux:自定义脚本
我原来的答案
Bash sn-p 用于在 ./app
文件夹中的所有 .py
文件中用制表符替换 4-空格缩进(脚本中有两个 4
)(递归):
find ./app -iname '*.py' -type f \
-exec awk -i inplace \
' match($0, /^(( 4)*)(.*?)$/, arr); gsub(/ 4/, "\t", arr[1]) ; print arr[1] arr[3] ' \;
它不会修改中间或末尾的 4 个空格。
在 Ubuntu 16.0x 和 Linux Mint 18 下测试
【讨论】:
【参考方案5】:如果您安装了 GNU coreutils,请考虑 %!unexpand --first-only
或对于 4 空格制表符,请考虑 %!unexpand -t 4 --first-only
(--first-only
存在,以防您不小心将 unexpand
与 --all
一起调用)。
请注意,这只会替换规定制表位之前的空格,而不是它们后面的空格;除非您更字面地显示选项卡,否则您不会在 vim 中看到任何视觉差异;例如,我的~/.vimrc
包含set list listchars=tab:▸┈
(我怀疑这就是你认为unexpand
不起作用的原因)。
【讨论】:
【参考方案6】:就我而言,我有多个空格(字段由一个或多个空格分隔),我想用制表符替换它们。以下是这样做的:
:% s/\s\+/\t/g
【讨论】:
【参考方案7】::%s/\(^\s*\)\@<= /\t/g
翻译:搜索 4 个连续空格的每个实例(在 = 字符之后),但前提是直到该点的整行都是空格(这使用零宽度后视断言,\@<=
)。用制表符替换每个找到的实例。
【讨论】:
将1个空格扩展到1个标签的唯一解决方案,但要注意答案中的unicode,我只是使用:%s/\(^\s*\)\@<= /\t/g
-在<=
之后放置适当数量的空格(将 4 个空格转换为 1 个制表符,放入 4 个空格)【参考方案8】:
简单的 Python 脚本:
import os
SOURCE_ROOT = "ROOT DIRECTORY - WILL CONVERT ALL UNDERNEATH"
for root, dirs, files in os.walk(SOURCE_ROOT):
for f in files:
fpath = os.path.join(root,f)
assert os.path.exists(fpath)
data = open(fpath, "r").read()
data = data.replace(" ", "\t")
outfile = open(fpath, "w")
outfile.write(data)
outfile.close()
【讨论】:
一个大问题:这个 sn-p 替换了 all 4 个空格,不仅仅是缩进(在行首) 第二个:文件过滤器没有实现【参考方案9】:使用 Vim 扩展所有 前导 空格(比 'tabstop'
更宽),你使用 retab
是正确的,但首先要确保 'expandtab'
被重置(:verbose set ts? et?
是你的朋友)。 retab
有一个范围,所以我通常指定%
表示“整个文件”。
:set tabstop=2 " To match the sample file
:set noexpandtab " Use tabs, not spaces
:%retab! " Retabulate the whole file
在做这样的事情之前(尤其是对 Python 文件!),我通常设置'list'
,以便我可以看到空格并进行更改。
为此,我在my .vimrc
中有以下映射:
nnoremap <F2> :<C-U>setlocal lcs=tab:>-,trail:-,eol:$ list! list? <CR>
【讨论】:
这是我如何让它工作的,不知道什么是必要的,什么不是,顺便说一句,我不知道 reatab 之前的“%”在做什么::set noet
,:set tabstop=2
、:retab!
、:%retab!
、:set tabstop=1
、:retab!
、:%retab!
我已经更新了我的答案,包括解释我为什么使用%
。 The F2 mapping is exactly as it's typed.
对于那个特定的文件,我会这样做::set noet ts=2 |%retab!
@Johnsyweb 公平地说,我错了::%retab!
仍然有效。我对==
等确实 尊重preserveindent 设置感到困惑。
要将咖啡脚本文件从空格转换为制表符,我遵循@Johnsyweb 的回答(包括添加到 .vimrc 文件中),而是使用:set tabstop=4
【参考方案10】:
1 - 如果您有空格并需要制表符。
首先,您需要确定一个制表符有多少个空格。也就是说,假设您有前导 4 个空格或 8 个空格的行...比您意识到您可能希望制表符为 4 个空格。现在有了这些信息,您可以:
:set ts=4
:set noet
:%retab!
这里有问题!这一系列命令将查找您的所有文本,而不仅仅是行首的空格。这意味着像"Hey,␣this␣␣␣␣is␣4␣spaces"
这样的字符串将变为"Hey,␣this⇥is␣4␣spaces"
,但不是!它是一个标签!。
为了解决这个小问题,我推荐search
,而不是retab
。
:%s/^\(^I*\)␣␣␣␣/\1^I/g
此搜索将在整个文件中查找以任意数量的制表符开头的任何行,后跟 4 个空格,并将其替换为找到的任何数量的制表符加一。
很遗憾,这不会立即运行!
首先,文件会有以空格开头的行。然后搜索将仅将前 4 个空格转换为制表符,并让以下...
您需要重复该命令。多少次?直到您收到pattern not found
。我还想不出一种方法来自动化这个过程。但如果你这样做:
`10@:`
你可能已经完成了。此命令重复最后一次搜索/替换 10 次。您的程序不太可能有这么多缩进。如果有,请再次重复@@
。
现在,只是为了完成答案。我知道您要求相反,但您永远不知道何时需要撤消操作。
2 - 你有制表符并且想要空格。
首先,确定您希望将制表符转换为多少个空格。假设您希望每个选项卡为 2 个空格。然后你做:
:set ts=2
:set et
:%retab!
这对字符串也会有同样的问题。但由于它更好的编程风格是不在字符串中使用硬制表符,所以你实际上在这里做了一件好事。如果您确实需要在字符串中使用制表符,请使用\t
。
【讨论】:
似乎是一种极为罕见的竞争状况。无论如何,创建一个接受视觉范围选择的函数并迭代搜索函数,直到找不到匹配项,我想这将是一个更聪明和有用的答案。 是的,或者那样。如果你想要一个更明确的解决方案,你可以使用一个函数。无论如何,知道如何在ex command
中操作总是好的,因为这就是函数内部的内容。不,它并不罕见。你只需要有带空格的字符串就可以了。一点也不罕见。我一直在那里。感谢您的评论。
请解释为什么 /g 不足以将每行连续空格中的所有 4 组转换为制表符,而不必多次运行搜索。
嗨@BjarturThorlacius,问题是如果您删除^
符号,从行首开始搜索,您可能会更改字符串中的空格。使用^
,您可以保证从行首开始只更改空格和制表符,因此,缩进。除此之外,如果您确定可以一次完成所有操作,请删除 ^
并只运行一次::%s/\(^I*\)␣␣␣␣/\1^I/g
以上是关于如何在 Vim 或 Linux 中将空格转换为制表符?的主要内容,如果未能解决你的问题,请参考以下文章
在 TextWrangler 中自动将制表符转换为 4 个空格?