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

我怎样才能将制表符转换为目录的每个文件中的空格(可能recursion)?

另外,有没有一种方法来设置每个标签的空格数?

警告:这将打破你的回购。

会破坏二进制文件 ,包括那些在svn.git下的文件 ! 阅读使用前的意见!

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

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

缺点:

  • 将replace文件中的标签。
  • 如果在这个目录中碰巧有一个5GB的SQL转储会花费很长时间。

sed简单replace是可以的,但不是最好的解决scheme。 如果选项卡之间存在“额外”空间,那么在replace之后,它们仍然会在那里,所以边距会变得不齐整。 在行中间展开的标签也无法正常工作。 在bash ,我们可以说

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

expand应用到当前目录树中的每个Java文件。 如果您要定位其他文件types,请删除/replace-name参数。 正如评论中提到的那样,在删除-name或使用弱通配符时要非常小心。 你可以轻松地clobber存储库和其他隐藏的文件没有意图。 这就是为什么最初的答案包括:

在尝试这样的事情之前,你应该总是做一个树的备份副本,以防出现问题。

尝试命令行工具expand

 expand -i -t 4 input > output 

哪里

  • -i用于只展开每行上的引导标签;
  • -t 4表示每个选项卡将被转换为4个空白字符(默认为8)。

最后,你可以使用Homebrew( brew install coreutilsbrew install coreutils后,在OSX上使用gexpand

使用反斜线转义的sed

在linux上:

  • 将所有选项卡replace为所有* .txt文件中的1个连字符:

     sed -i $'s/\t/-/g' *.txt 
  • 用所有* .txt文件中的1个空格replace所有选项卡:

     sed -i $'s/\t/ /g' *.txt 
  • 用所有* .txt文件中的4个空格replace所有制表符:

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

在Mac上:

  • 用所有* .txt文件中的4个空格replace所有制表符:

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

从Gene的回答中收集最好的评论,迄今为止最好的解决scheme是使用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"' {} \; 

说明:

  • ./正在从当前目录recursionsearch
  • -iname是不区分大小写的匹配(对于*.java*.JAVA喜欢)
  • type -f只能find常规文件(无目录,二进制文件或符号链接)
  • -exec bash -c在每个文件名的子shell中执行以下命令, {}
  • expand -t 4将所有TAB扩展到4个空格
  • sponge吸收标准input(从expand )并写入文件(同一个)*。

注意 :*简单的文件redirect( > "$0" )在这里不起作用,因为它会过快地覆盖文件 。

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

我喜欢上面的recursion应用程序的“查找”例子。 为了适应它是非recursion的,只改变当前目录中匹配通配符的文件,shell glob扩展对于less量的文件就足够了:

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

如果你想在它信任之后保持沉默,最后在sh命令中放一个-v

当然你可以select第一个命令中的任何一组文件。 例如,像这样以受控的方式仅列出特定的子目录(或多个目录):

 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 

我怎样才能将制表符转换为目录的每个文件中的空格(可能recursion)?

这通常不是你想要的。

你想这样做的PNG图像? PDF文件? .git目录? 你的Makefile需要制表符)? 一个5GB的SQL转储?

从理论上说,你可以通过很多排除选项来find你正在使用的东西; 但这是脆弱的,只要添加其他二进制文件就会中断。

你想要的,至less是:

  1. 跳过一定大小的文件。
  2. 通过检查是否存在NULL字节来检测文件是否为二进制文件。
  3. 只能replace文件开头的选项卡( expand这个, sed不会)。

据我所知,没有一个“标准的”Unix工具可以做到这一点,而且用一个shell来做并不是很容易,所以需要一个脚本。

前一段时间,我创build了一个名为sanitize_files的小脚本,就是这样做的。 它还修复了其他一些常见的东西,例如用\r\nreplace\r\n \n ,添加尾部的\n等。

您可以在下面find一个没有额外function和命令行参数的简化脚本,但是我build议您使用上面的脚本,因为它更有可能接收错误修正和其他更新。

我还想指出,为了回应这里的一些其他答案,使用shell globbing 不是一个强有力的方法,因为迟早你会得到比ARG_MAX更多的文件(on现代的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混合的制表符和空格后,我使用astyle重新缩进了所有的C / C ++代码。 如果你愿意的话,它也可以select强制特定的花括号。

我的build议是使用:

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

注释:

  1. 使用就地编辑。 保持VCS中的备份。 不需要生成* .orig文件。 无论如何,最好的做法是将结果与最后一次提交进行比较,以确保其按预期工作。
  2. sed是一个stream编辑器。 使用ex进行现场编辑。 这样可以避免在顶级答案中为每个replace创build额外的临时文件和产卵shell。
  3. 警告:这与所有选项卡混杂,不仅用于缩进。 此外,它不会做上下文感知replace选项卡。 这对我的用例来说已经足够了。 但是对你来说可能是不可接受的。
  4. 编辑:这个答案的早期版本使用find|xargs而不是find -exec 。 正如@ gniourf-gniourf指出的那样,这会导致文件名中的空格,引号和控制字符的问题。 惠勒 。

要将目录中的所有Java文件recursion转换为使用4个空格而不是一个制表符:

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

你可以使用普遍可用的pr命令(手册页在这里 )。 例如,要将制表符转换为四个空格,请执行以下操作:

 pr -t -e=4 file > file.expanded 
  • -t禁止标题
  • -e=num将制表符扩展为num空格

以recursion方式转换目录树中的所有文件,同时跳过二进制文件:

 #!/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回购中是危险的
  2. 如果您的代码文件中的选项卡embeddedstring文字,这不是正确的解决scheme

下载并运行以下脚本以recursion方式将硬标签转换为纯文本文件中的软标签。

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

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

一个可以使用vim的:

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

正如Carpetsmoker所说的那样,它会根据你的vim设置进行复制。 如果有的话,在文件模式。 而且,它不仅会在行首开始replace制表符。 这不是你一般想要的。 例如,你可能有文字,包含标签。

如果要将制表符replace为空格,例如* .c文件和* .h文件,则下一个命令是最好的: find . -name "*.c" -o -name "*.h" | xargs -IFILE -t bash -c " expand -t 4 FILE > tmp ; cat tmp > FILE" find . -name "*.c" -o -name "*.h" | xargs -IFILE -t bash -c " expand -t 4 FILE > tmp ; cat tmp > FILE"

然后rm -f tmp

你可以使用tabs-to-spaces包来查找这个。

首先,安装tabs-to-spaces

 npm install -g tabs-to-spaces 

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

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

这将在每个文件中用2个spacesreplace每个tab符。

只需在“.lua”文件中将制表符转换为空格[制表符 – > 2个空格]

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

使用vim-way:

 $ ex +'bufdo retab' -cxa **/*.* 
  • 做好备份! 在执行上述命令之前,因为它可能会损坏您的二进制文件。
  • 要使用globstar** )进行recursion,请激活shopt -s globstar
  • 要指定特定的文件types,请使用例如: **/*.c

要修改tabstop,请添加+'set ts=2'

然而不利的一面是它可以代替string内的标签 。

因此,对于稍微好一点的解决scheme(使用替代),请尝试:

 $ 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 $* } 

其他答案中提出的expand的使用似乎是单独这个任务最合乎逻辑的方法。

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

如果使用Bash 4.0或更高版本, 内置的 globstar可以用来recursionsearch**

使用GNU Awk版本4.1或更高版本,sed就像“就地”文件修改:

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

如果你想设置每个标签的空格数量:

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

如果你不介意使用记事本++或类似的软件,打开记事本++中提到的所有文件,然后去replace选项卡(快捷键Ctrl + H)

select正则expression式或扩展search模式,现在它不能\ t,把它放在search,并放置任何数量的空间,你想要replace,点击全部replace所有打开的文件。

用记事本++中的空格replace标签

还有一种方法可以更好的满足你的需要,你可以在查找文件标签中做到这一点,你可以select一个目录,并可以使用typesfilterselect特定的文件,其余的就像以前一样。