如何将目录的每个文件中的制表符转换为空格(可能是递归的)?
另外,有没有办法设置每个选项卡的空格数?
简单的替换sed
是可以的,但不是最好的解决方案。如果选项卡之间有“额外”空格,它们在替换后仍然存在,因此边距将参差不齐。在行中间展开的选项卡也将无法正常工作。在bash
中,我们可以改为
find . -name '*.java' ! -type d -exec bash -c 'expand -t 4 "$0" > /tmp/e && mv /tmp/e "$0"' {} \;
应用于expand
当前目录树中的每个 Java 文件。-name
如果您针对某些其他文件类型,请删除/替换参数。-name
正如其中一条评论所提到的,在删除或使用弱通配符时要非常小心。您可以无意识地轻松破坏存储库和其他隐藏文件。这就是为什么原始答案包括以下内容的原因:
在尝试这样的事情之前,您应该始终制作树的备份副本,以防出现问题。
试试命令行工具expand
。
expand -i -t 4 input | sponge output
在哪里
-i
用于仅展开每行上的前导制表符;-t 4
意味着每个选项卡将被转换为 4 个空白字符(默认为 8 个)。sponge
来自moreutils
包,并避免清除输入文件。在 macOS 上,该软件包moreutils
可通过Homebrew ( brew install moreutils
) 或MacPorts ( sudo port install moreutils
) 获得。最后,您可以在使用Homebrew ( ) 或MacPorts ( )gexpand
安装后在 macOS 上使用。coreutils
brew install coreutils
sudo port install coreutils
警告:这会破坏你的回购。
这将损坏二进制文件,包括
svn
,.git
!使用前请阅读评论!
find . -iname '*.java' -type f -exec sed -i.orig 's/\t/ /g' {} +
原始文件另存为[filename].orig
.
将“*.java”替换为您要查找的文件类型的文件结尾。这样可以防止二进制文件意外损坏。
缺点:
从Gene 的答案中收集最好的评论,迄今为止最好的解决方案是使用sponge
from moreutils。
sudo apt-get install moreutils
# The complete one-liner:
find ./ -iname '*.java' -type f -exec bash -c 'expand -t 4 "$0" | sponge "$0"' {} \;
解释:
./
正在从当前目录递归搜索-iname
是不区分大小写的匹配项(对于两者*.java
和*.JAVA
喜欢)type -f
仅查找常规文件(无目录、二进制文件或符号链接)-exec bash -c
在子shell中为每个文件名执行以下命令,{}
expand -t 4
将所有 TAB 扩展为 4 个空格sponge
吸收标准输入(来自expand
)并写入文件(同一个)*。注意:* 简单的文件重定向 ( > "$0"
) 在这里不起作用,因为它会很快覆盖文件。
优点:保留所有原始文件权限,不tmp
使用中间文件。
使用反斜杠转义sed
。
在 Linux 上:
在所有 *.txt 文件中将所有选项卡替换为 1 个连字符:
sed -i $'s/\t/-/g' *.txt
在所有 *.txt 文件中将所有选项卡替换为 1 个空格:
sed -i $'s/\t/ /g' *.txt
在所有 *.txt 文件中将所有制表符替换为 4 个空格:
sed -i $'s/\t/ /g' *.txt
在 Mac 上:
在所有 *.txt 文件中将所有制表符替换为 4 个空格:
sed -i '' $'s/\t/ /g' *.txt
您可以使用普遍可用的pr
命令(此处的手册页)。例如,要将制表符转换为四个空格,请执行以下操作:
pr -t -e=4 file > file.expanded
-t
抑制标题-e=num
将制表符扩展到num
空格递归转换目录树中的所有文件,同时跳过二进制文件:
#!/bin/bash
num=4
shopt -s globstar nullglob
for f in **/*; do
[[ -f "$f" ]] || continue # skip if not a regular file
! grep -qI "$f" && continue # skip binary files
pr -t -e=$num "$f" > "$f.expanded.$$" && mv "$f.expanded.$$" "$f"
done
跳过二进制文件的逻辑来自这篇文章。
笔记:
我喜欢上面递归应用程序的“查找”示例。为了使其成为非递归的,仅更改当前目录中匹配通配符的文件,shell glob 扩展对于少量文件就足够了:
ls *.java | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh -v
如果您希望它在您相信它可以工作后保持沉默,只需在最后删除-v
命令sh
即可。
当然,您可以在第一个命令中选择任何一组文件。例如,以受控方式仅列出特定的子目录(或目录),如下所示:
ls mod/*/*.php | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh
或者依次运行 find(1) 与深度参数等的某种组合:
find mod/ -name '*.php' -mindepth 1 -maxdepth 2 | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh
您可以为此使用find
with tabs-to-spaces
package。
一、安装tabs-to-spaces
npm install -g tabs-to-spaces
然后,从项目的根目录运行此命令;
find . -name '*' -exec t2s --spaces 2 {} \;
这会将每个文件中的每个tab
字符替换为 2 spaces
。
如何将目录的每个文件中的制表符转换为空格(可能是递归的)?
这通常不是你想要的。
您想为 png 图像执行此操作吗?PDF文件?.git 目录?你的
Makefile
(需要标签)?一个 5GB 的 SQL 转储?
从理论上讲,您可以将大量排除选项传递给find
您正在使用的任何其他选项;但这很脆弱,一旦添加其他二进制文件就会中断。
你想要的,至少是:
expand
的制表符(这样做,sed
不这样做)。据我所知,没有“标准”的 Unix 实用程序可以做到这一点,而且使用 shell 单行也不是很容易,所以需要一个脚本。
不久前,我创建了一个名为
sanitize_files的小脚本,它正是这样做的。它还修复了一些其他常见的东西,比如替换\r\n
为\n
、添加尾随\n
等。
您可以在下面找到一个没有额外功能和命令行参数的简化脚本,但我建议您使用上面的脚本,因为它更有可能收到错误修复和其他更新而不是这篇文章。
我还想指出,作为对这里的其他一些答案的回应,使用 shell globbing并不是一种可靠的方法,因为迟早你会得到比适合更多的文件ARG_MAX
(在现代Linux 系统是 128k,可能看起来很多,但迟早是不够
的)。
#!/usr/bin/env python
#
# http://code.arp242.net/sanitize_files
#
import os, re, sys
def is_binary(data):
return data.find(b'\000') >= 0
def should_ignore(path):
keep = [
# VCS systems
'.git/', '.hg/' '.svn/' 'CVS/',
# These files have significant whitespace/tabs, and cannot be edited
# safely
# TODO: there are probably more of these files..
'Makefile', 'BSDmakefile', 'GNUmakefile', 'Gemfile.lock'
]
for k in keep:
if '/%s' % k in path:
return True
return False
def run(files):
indent_find = b'\t'
indent_replace = b' ' * indent_width
for f in files:
if should_ignore(f):
print('Ignoring %s' % f)
continue
try:
size = os.stat(f).st_size
# Unresolvable symlink, just ignore those
except FileNotFoundError as exc:
print('%s is unresolvable, skipping (%s)' % (f, exc))
continue
if size == 0: continue
if size > 1024 ** 2:
print("Skipping `%s' because it's over 1MiB" % f)
continue
try:
data = open(f, 'rb').read()
except (OSError, PermissionError) as exc:
print("Error: Unable to read `%s': %s" % (f, exc))
continue
if is_binary(data):
print("Skipping `%s' because it looks binary" % f)
continue
data = data.split(b'\n')
fixed_indent = False
for i, line in enumerate(data):
# Fix indentation
repl_count = 0
while line.startswith(indent_find):
fixed_indent = True
repl_count += 1
line = line.replace(indent_find, b'', 1)
if repl_count > 0:
line = indent_replace * repl_count + line
data = list(filter(lambda x: x is not None, data))
try:
open(f, 'wb').write(b'\n'.join(data))
except (OSError, PermissionError) as exc:
print("Error: Unable to write to `%s': %s" % (f, exc))
if __name__ == '__main__':
allfiles = []
for root, dirs, files in os.walk(os.getcwd()):
for f in files:
p = '%s/%s' % (root, f)
if do_add:
allfiles.append(p)
run(allfiles)
我的建议是使用:
find . -name '*.lua' -exec ex '+%s/\t/ /g' -cwq {} \;
注释:
在找到混合制表符和空格后,我曾经astyle
重新缩进所有 C/C++ 代码。如果您愿意,它还具有强制特定支撑样式的选项。
可以使用vim
:
find -type f \( -name '*.css' -o -name '*.html' -o -name '*.js' -o -name '*.php' \) -execdir vim -c retab -c wq {} \;
正如 Carpetsmoker 所说,它会根据您的设置重新vim
设置标签。和文件中的模式,如果有的话。此外,它不仅会替换行首的制表符。这不是您通常想要的。例如,您可能有包含制表符的文字。
要递归地将目录中的所有 Java 文件转换为使用 4 个空格而不是制表符:
find . -type f -name *.java -exec bash -c 'expand -t 4 {} > /tmp/stuff;mv /tmp/stuff {}' \;
没有提到身体rpl
?使用 rpl 您可以替换任何字符串。要将制表符转换为空格,
rpl -R -e "\t" " " .
很简单。
下载并运行以下脚本,以递归方式将纯文本文件中的硬标签转换为软标签。
从包含纯文本文件的文件夹中执行脚本。
#!/bin/bash
find . -type f -and -not -path './.git/*' -exec grep -Iq . {} \; -and -print | while read -r file; do {
echo "Converting... "$file"";
data=$(expand --initial -t 4 "$file");
rm "$file";
echo "$data" > "$file";
}; done;
Git存储库友好方法
git-tab-to-space() (
d="$(mktemp -d)"
git grep --cached -Il '' | grep -E "${1:-.}" | \
xargs -I'{}' bash -c '\
f="${1}/f" \
&& expand -t 4 "$0" > "$f" && \
chmod --reference="$0" "$f" && \
mv "$f" "$0"' \
'{}' "$d" \
;
rmdir "$d"
)
作用于当前目录下的所有文件:
git-tab-to-space
仅作用于 C 或 C++ 文件:
git-tab-to-space '\.(c|h)(|pp)$'
你可能特别想要这个,因为那些烦人的 Makefiles 需要标签。
命令git grep --cached -Il ''
:
.git
如解释:如何列出 git 存储库中的所有文本(非二进制)文件?
chmod --reference
保持文件权限不变:https ://unix.stackexchange.com/questions/20645/clone-ownership-and-permissions-from-another-file不幸的是我找不到简洁的 POSIX 替代方案。
如果您的代码库有允许在字符串中使用功能性原始选项卡的疯狂想法,请使用:
expand -i
然后玩得开心一一浏览所有非行首选项卡,您可以列出这些选项卡:Is it possible to git grep for tabs?
在 Ubuntu 18.04 上测试。
The use of expand
as suggested in other answers seems the most logical approach for this task alone.
That said, it can also be done with Bash and Awk in case you may want to do some other modifications along with it.
If using Bash 4.0 or greater, the shopt builtin globstar
can be used to search recursively with **
.
With GNU Awk version 4.1 or greater, sed like "inplace" file modifications can be made:
shopt -s globstar
gawk -i inplace '{gsub("\t"," ")}1' **/*.ext
In case you want to set the number of spaces per tab:
gawk -i inplace -v n=4 'BEGIN{for(i=1;i<=n;i++) c=c" "}{gsub("\t",c)}1' **/*.ext
使用 vim 方式:
$ ex +'bufdo retab' -cxa **/*.*
globstar
( **
) 进行递归,请通过 激活shopt -s globstar
。**/*.c
.要修改制表位,请添加+'set ts=2'
.
但是缺点是它可以替换字符串中的制表符。
因此,对于稍微更好的解决方案(通过使用替换),请尝试:
$ ex -s +'bufdo %s/^\t\+/ /ge' -cxa **/*.*
或者通过使用ex
编辑器+expand
实用程序:
$ ex -s +'bufdo!%!expand -t2' -cxa **/*.*
对于尾随空格,请参阅:如何删除多个文件的尾随空格?
您可以将以下功能添加到您的.bash_profile
:
# Convert tabs to spaces.
# Usage: retab *.*
# See: https://stackoverflow.com/q/11094383/55075
retab() {
ex +'set ts=2' +'bufdo retab' -cxa $*
}
仅在“.lua”文件中将制表符转换为空格 [制表符 -> 2 个空格]
find . -iname "*.lua" -exec sed -i "s#\t# #g" '{}' \;