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

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


当前回答

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

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

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

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

其他回答

您可以使用通常可用的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

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

注意:

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

在其他答案中建议的使用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

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

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

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

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

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保持文件权限不变:https://unix.stackexchange.com/questions/20645/clone-ownership-and-permissions-from-another-file不幸的是,我找不到一个简洁的POSIX替代方案。

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

expand -i

然后一个接一个地浏览所有非行开头的制表符,你可以列出:是否可能为制表符git grep ?

在Ubuntu 18.04上测试。

从Gene的回答中收集最好的评论,到目前为止最好的解决方案是使用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个空格 海绵吸收标准输入(从扩展)并写入文件(同一文件)*。

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

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