如何将制表符转换为一个目录的每个文件中的空格?

如何将制表符转换为一个目录的每个文件中的空格(可能递归)?

此外,是否有一种方法来设置每个制表符的空格数?

182405 次浏览

尝试命令行工具expand

expand -i -t 4 input | sponge output

在哪里

  • -i只用于展开每行的前导制表符;
  • -t 4意味着每个制表符将被转换为4个空格字符(默认为8个)。
  • sponge来自moreutils包,避免了清除输入文件。在macOS上,moreutils包可以通过家酿 (brew install moreutils)或MacPorts (sudo port install moreutils)获得。

最后,在使用家酿 (brew install coreutils)或MacPorts (sudo port install coreutils)安装coreutils之后,可以在macOS上使用gexpand

警告:这会破坏你的回购。

这个会损坏二进制文件,包括那些在svn下,.git!使用前请阅读评论!

find . -iname '*.java' -type f -exec sed -i.orig 's/\t/ /g' {} +

原始文件保存为[filename].orig

将'*.java'替换为您正在寻找的文件类型的文件结尾。这样可以防止二进制文件的意外损坏。

缺点:

  • 将替换文件中的所有制表符。
  • 如果在这个目录中有一个5GB的SQL转储,将花费很长时间。

简单地替换为sed是可以的,但不是最好的解决方案。如果制表符之间有“额外的”空格,替换后它们仍然在那里,因此页边距将是粗糙的。在行中间展开的制表符也不能正常工作。在bash中,我们可以改为

find . -name '*.java' ! -type d -exec bash -c 'expand -t 4 "$0" > /tmp/e && mv /tmp/e "$0"' {} \;

expand应用到当前目录树中的每个Java文件。如果目标是其他文件类型,则删除/替换-name参数。正如其中一条评论提到的,在删除-name或使用弱通配符时要非常小心。你可以很容易地破坏存储库和其他隐藏的文件。这就是为什么最初的答案是这样的:

在尝试这样做之前,您应该始终对树进行备份,以防出现错误。

在发现混合制表符和空格后,我使用astyle重新缩进所有C/ c++代码。如果您愿意,它还可以强制使用特定的括号样式。

将制表符转换为空格只是在”。Lua " files [tab -> 2个空格]

find . -iname "*.lua" -exec sed -i "s#\t#  #g" '{}' \;

对于递归应用程序,我喜欢上面的“find”示例。为了使其具有非递归性,只更改当前目录中匹配通配符的文件,shell glob扩展可以满足少量文件的需求:

ls *.java | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh -v

如果你想让它在你相信它工作后保持沉默,只需在结尾的sh命令上删除-v

当然,您可以在第一个命令中选择任何一组文件。例如,以受控的方式只列出一个特定的子目录(或多个目录),如下所示:

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

可以使用vim来实现:

find -type f \( -name '*.css' -o -name '*.html' -o -name '*.js' -o -name '*.php' \) -execdir vim -c retab -c wq {} \;

正如Carpetsmoker所述,它将根据你的vim设置重新标签。文件中的modeline(如果有的话)。此外,它不仅将替换行首的制表符。这通常不是你想要的。例如,你可能有文字,包含制表符。

在其他答案中建议使用expand似乎是完成这项任务最合理的方法。

也就是说,它也可以用Bash和Awk来完成,以防你想在它的同时做一些其他的修改。

如果使用Bash 4.0或更高版本,shopt装入的 globstar可用于使用**进行递归搜索。

在GNU Awk 4.1或更高版本中,sed可以像“inplace”一样修改文件:

shopt -s globstar
gawk -i inplace '{gsub("\t","    ")}1' **/*.ext

如果你想设置每个制表符的空格数:

gawk -i inplace -v n=4 'BEGIN{for(i=1;i<=n;i++) c=c" "}{gsub("\t",c)}1' **/*.ext

使用vim-way:

$ ex +'bufdo retab' -cxa **/*.*
  • 备份!在执行上述命令之前,因为它会破坏你的二进制文件。
  • 要使用globstar (**)进行递归,请通过shopt -s globstar激活。
  • 要指定特定的文件类型,使用例如:**/*.c

修改tabstop,添加+'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 $*
}

我如何将制表符转换为一个目录的每个文件的空格(可能 递归地)?< / p >

这通常是你想要的

你想对png图像做这个吗?PDF文件吗?.git目录?你的 Makefile(哪个需要选项卡)?一个5GB的SQL转储?< / p > 理论上,你可以将一大堆排除选项传递给find或其他参数 否则你在使用;但是这个是易碎的,只要你再加一个就会碎 二进制文件。< / p >

你想要的至少是:

  1. 跳过超过一定大小的文件。
  2. 通过检查NULL字节的存在来检测文件是否为二进制。
  3. 只替换文件的开始处的制表符(expand这样做,sed . b) 李不)。< / >

据我所知,没有“标准”的Unix实用程序可以做到这一点,而且用shell一行程序来做到这一点并不容易,因此需要一个脚本。

不久前,我创建了一个名为 sanitize_files,完全正确 那它还修复了一些其他常见的问题,比如用\n替换\r\n

.

. 你可以在下面找到一个简化的脚本没有额外的功能和命令行参数,但是我 建议您使用上面的脚本,因为它更有可能收到错误修复和

我还想指出,作为对这里其他答案的回应, 使用shell globbing是一个健壮的方法,因为更快 或者稍后你会得到比ARG_MAX (on modern Linux系统是128k,这可能看起来很多,但迟早会是 足够的)。< / p >


#!/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)

使用反斜杠转义的sed

在linux上:

  • 将所有*.txt文件中的所有制表符替换为1个连字符:

    sed -i $'s/\t/-/g' *.txt
    
  • Replace all tabs with 1 space inplace, in all *.txt files:

    sed -i $'s/\t/ /g' *.txt
    
  • Replace all tabs with 4 spaces inplace, in all *.txt files:

    sed -i $'s/\t/    /g' *.txt
    

On a mac:

  • Replace all tabs with 4 spaces inplace, in all *.txt files:

    sed -i '' $'s/\t/    /g' *.txt
    

我的建议是使用:

find . -name '*.lua' -exec ex '+%s/\t/  /g' -cwq {} \;

评论:

  1. 使用就地编辑。在VCS中保存备份。不需要生产*。源自文件。在任何情况下,将结果与上一次提交进行比较都是一种很好的实践,以确保它能按预期工作。
  2. sed是一个流编辑器。使用ex进行就地编辑。这避免了创建额外的临时文件和为每次替换生成外壳,就像上面的回答中那样。
  3. 警告:这将混淆所有制表符,而不仅仅是用于缩进的制表符。它也不做上下文感知的选项卡替换。这对于我的用例来说已经足够了。但你可能无法接受。
  4. 编辑:这个答案的早期版本使用find|xargs而不是find -exec。正如@gniourf-gniourf指出的那样,这会导致文件名cf. 惠勒中的空格、引号和控制字符出现问题。

将目录下的所有Java文件递归转换为4个空格而不是制表符:

find . -type f -name *.java -exec bash -c 'expand -t 4 {} > /tmp/stuff;mv /tmp/stuff {}' \;

为此,你可以使用findtabs-to-spaces包。

首先,安装tabs-to-spaces

npm install -g tabs-to-spaces

然后,从项目的根目录运行这个命令;

find . -name '*' -exec t2s --spaces 2 {} \;

这将把每个文件中的每个tab字符替换为2个spaces

下载并运行以下脚本,递归地将纯文本文件中的硬选项卡转换为软选项卡。

在包含纯文本文件的文件夹中执行脚本。

#!/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;

基因的回答收集最好的注释,到目前为止最好的解决方案是使用moreutils中的sponge

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中为每个文件名{} . -exec bash -c执行以下命令
  • expand -t 4将所有制表符扩展为4个空格
  • sponge吸收标准输入(来自expand)并写入文件(同一文件)*。

请注意: *一个简单的文件重定向(> "$0")将不能在这里工作,因为它会很快覆盖文件

优势:保留所有原始文件权限,不使用中间的tmp文件。

你可以使用一般可用的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

跳过二进制文件的逻辑来自这篇文章

注意:

  1. 在git或svn repo中这样做可能是危险的
  2. 如果您的代码文件在字符串字面值中嵌入了空白制表符,那么这不是正确的解决方案

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)$'

您可能特别需要这个,因为那些讨厌的makefile需要选项卡。

命令git grep --cached -Il '':

  • 只列出被跟踪的文件,所以.git内没有任何内容
  • 不包括目录、二进制文件(将被损坏)和符号链接(将被转换为常规文件)

正如在如何列出一个git存储库中的所有文本(非二进制)文件?中解释的那样

chmod --reference保持文件权限不变:

如果你的代码库有一个疯狂的想法,允许在字符串中使用功能原始制表符,使用:

expand -i

然后逐个浏览所有非行开始的制表符,你可以用:是否有可能为标签git grep ?列出

在Ubuntu 18.04上测试。

没有人提到rpl?使用rpl可以替换任何字符串。 将制表符转换为空格,

rpl -R -e "\t" "    "  .

非常简单。