如何递归地删除所有文件的尾部空格?

142

如何删除整个项目中所有源代码末尾的空格?从根目录开始,移除所有文件夹中所有文件中的末尾空格。

此外,我希望能够直接修改文件,而不仅仅是将所有内容打印到标准输出流中。


哦,你是在寻找“便携式”解决方案还是更适用于特定操作系统的解决方案?你使用的是什么操作系统? - Joe Pineda
3
我很想看到这个能在OS X Snow Leopard上运行的版本,并且会忽略.git和.svn文件夹。 - Trevor Turk
15个回答

90

这里是针对OS X 10.6及以上版本的解决方案。

它会忽略.git和.svn文件夹及其内容。同时不会留下备份文件。

(export LANG=C LC_CTYPE=C
find . -not \( -name .svn -prune -o -name .git -prune \) -type f -print0 | perl -0ne 'print if -T' | xargs -0 sed -Ei 's/[[:blank:]]+$//'
)

封闭的括号保留了当前shell中L*变量 - 在子shell中执行。

11
在替换字符串中使用\+代替*可以让它更快-否则它会匹配每一行的内容。 - l0b0
11
你可以使用[[:blank:]]来删除制表符和空格。 - Leif Gruenwoldt
21
在Mountain Lion上,这个命令返回 sed: RE error: illegal byte sequence 错误。 - Bryson
13
如果你的电脑出现“非法字节序列”的问题:输入 export LANG=C 再试一次。 - Georg Ledermann
3
在OS X 10.9上,我还需要添加export LC_CTYPE=C ,就像在这里找到的一样:https://dev59.com/C2Ik5IYBdhLWcg3we-H5。 - kissgyorgy
显示剩余9条评论

40

使用:

find . -type f -print0 | xargs -0 perl -pi.bak -e 's/ +$//'

如果您不想生成“.bak”文件:

find . -type f -print0 | xargs -0 perl -pi -e 's/ +$//'
作为zsh用户,您可以省略对find的调用,而改用以下命令:

as a zsh user, you can omit the call to find, and instead use:

perl -pi -e 's/ +$//' **/*

注意:为了防止破坏 .git 目录,请尝试添加:-not -iwholename '*.git*'


51
不要在 Git 存储库中尝试此操作,因为这可能会损坏 Git 的内部存储。 - mgold
15
太晚了,呜呜;/ - kenorb
4
请注意,可以在git仓库的子文件夹内运行此操作,但不能在任何包含git仓库(或其子目录)的文件夹内运行,即不能在包含.git文件夹的任何文件夹内运行,无论嵌套多深。 - Illya Moskvin
2
将此答案与 @deepwell 的答案结合起来,以避免 git/svn 问题 find . -not \( -name .svn -prune -o -name .git -prune \) -type f -print0 | xargs -0 perl -pi -e 's/ +$//' - William Denniss
1
可能有更好的方法,但是我通过在单独的文件夹中克隆出仓库,然后执行 rsync -rv --exclude=.git repo/ repo2/ 命令来恢复了被损坏的 git 仓库。这样,在 repo 中的本地更改也会出现在(未受损的)repo2 中。 - MatrixManAtYrService
我不小心运行了这个程序,结果弄乱了我的.git/index文件。通常情况下可以使用https://dev59.com/KFYN5IYBdhLWcg3wqpmH#47109640来修复该问题。 - CervEd

37

有两种替代方法,它们也适用于 DOS 换行符 (CR/LF),并且在避免二进制文件方面表现得相当不错:

通用解决方案,检查 MIME 类型是否以 text/ 开头:

while IFS= read -r -d '' -u 9
do
    if [[ "$(file -bs --mime-type -- "$REPLY")" = text/* ]]
    then
        sed -i 's/[ \t]\+\(\r\?\)$/\1/' -- "$REPLY"
    else
        echo "Skipping $REPLY" >&2
    fi
done 9< <(find . -type f -print0)

Mat 提供的 Git 版本库特定 解决方案,使用 git grep-I 选项跳过 Git 认为是二进制的文件:
git grep -I --name-only -z -e '' | xargs -0 sed -i 's/[ \t]\+\(\r\?\)$/\1/'

4
我很喜欢这个Git解决方案,我认为它应该排在首位。尽管我不想保存回车符号,但我仍然更喜欢它,比起我在2010年所结合的那一个。 - odinho - Velmont
我的git抱怨-e表达式为空,但使用-e '.*'非常好 - muirbot
@okor 在GNU sed中,-i的后缀选项是可选的,但在BSD sed中不是。严格来说,在这里它并不是必要的,所以我会将其删除。 - l0b0

28
在Bash中: find dir -type f -exec sed -i 's/ *$//' '{}' ';' 注意:如果您使用的是.git存储库,请尝试添加:-not -iwholename '.git'

对于找到的每个文件,这会生成类似于以下内容的错误。sed:1:“dir/file.txt”:命令a需要跟随文本 - iamjwc
4
为了删除所有空白字符而不仅仅是空格,你应该在sed正则表达式中将空格字符替换为[:space:]。 - WMR
这是更快更安全的变种:find dir -type f -print0 | xargs -r0 sed -i 's/ *$//' - pixelbeat
3
这破坏了我的 Git :( - CrabMan
你几乎肯定不想包含隐藏文件夹(例如各种临时文件、.git、.svn、.vscode、.idea),也不想包括node模块或类似的第三方包文件夹... find . -type f -name "*.scss" -regextype posix-extended -not -regex ".*\/(\.|node_modules).*"。如果,_只有输出看起来完全正常,那么_附加-exec部分...(自然而然地,在备份文件夹之前进行备份永远不会有害) - Frank N
显示剩余2条评论

14

这对我在OSX 10.5 Leopard上运行成功,该系统不使用GNU sed或xargs。

find dir -type f -print0 | xargs -0 sed -i.bak -E "s/[[:space:]]*$//"

如果您有需要排除的文件,请小心处理这个问题(我就遇到了)!

您可以使用-prune来忽略某些目录或文件。对于Git存储库中的Python文件,您可以使用类似以下形式的命令:

find dir -not -path '.git' -iname '*.py'

你能否澄清一下这个问题?我想要一个命令,可以递归地删除目录中所有文件的尾随空格,同时忽略“.git”目录。我无法完全理解你的示例... - Trevor Turk
如果您正在使用tcsh,则需要将双引号更改为单引号。否则,您将收到“非法变量名”错误。 - Brandon Fosdick
GNU sed 与之类似,但您可以使用 -i.bak 或 --in-place=.bak 参数,在执行完整命令 find dir -not -path '.git' -iname '*.py' -print0 | xargs -0 sed --in-place=.bak 's/[[:space:]]*$//' 后会生成备份文件。请将 dir 替换为要递归的顶级目录名。 - David Gardner
sed -i .bak?难道不应该是sed -i.bak(没有空格)吗? - Ondra Žižka

14
Ack是为这种任务而设计的。
它的工作方式与grep类似,但它知道不要进入.svn、.git、.cvs等地方。
ack --print0 -l '[ \t]+$' | xargs -0 -n1 perl -pi -e 's/[ \t]+$//'

比使用find/grep更容易。
通过大多数包管理器可以获得Ack(作为ackack-grep)。
它只是一个Perl程序,因此也有单文件版本可供下载和运行。请参见:Ack安装

ack非常棒。我已经使用它多年了,而且在大多数发行版的软件包仓库中都可以找到。 - Felipe Alvarez

9

ex

尝试使用Ex editor(Vim的一部分):

$ ex +'bufdo!%s/\s\+$//e' -cxa **/*.*

注意:对于递归(bash4和zsh),我们使用新的globbing选项** / *。* )。通过 shopt -s globstar 启用。 您可以将以下函数添加到 .bash_profile 中:
# Strip trailing whitespaces.
# Usage: trim *.*
# See: https://dev59.com/mWgv5IYBdhLWcg3wSe62
trim() {
  ex +'bufdo!%s/\s\+$//e' -cxa $*
}

sed

使用 sed,请查看:如何使用sed删除行尾空格?

find

查找以下脚本(例如remove_trail_spaces.sh),用于从文件中删除行尾空格:

#!/bin/sh
# Script to remove trailing whitespace of all files recursively
# See: https://dev59.com/y3VC5IYBdhLWcg3w51ny

case "$OSTYPE" in
  darwin*) # OSX 10.5 Leopard, which does not use GNU sed or xargs.
    find . -type f -not -iwholename '*.git*' -print0  | xargs -0 sed -i .bak -E "s/[[:space:]]*$//"
    find . -type f -name \*.bak -print0 | xargs -0 rm -v
    ;;
  *)
    find . -type f -not -iwholename '*.git*' -print0 | xargs -0 perl -pi -e 's/ +$//'
esac

请在您想要扫描的目录中运行此脚本。在OSX上,它将删除所有以.bak结尾的文件。

或者只需:

find . -type f -name "*.java" -exec perl -p -i -e "s/[ \t]$//g" {} \;

这是Spring Framework Code Style推荐的方式。


find . -type f -name "*.java" -exec perl -p -i -e "s/[ \t]$//g" {} \;" 只会移除一个尾随空格而不是全部。 - Kalle Richter

6

不要排除文件,以下是一种变化的方法,明确地列出了您想要剥离的文件,根据文件扩展名进行白名单过滤,随意适应您的需要:

find . \( -name *.rb -or -name *.html -or -name *.js -or -name *.coffee -or \
-name *.css -or -name *.scss -or -name *.erb -or -name *.yml -or -name *.ru \) \
-print0 | xargs -0 sed -i '' -E "s/[[:space:]]*$//"

1
为了让这对我起作用,我需要添加引号:-name "*.rb*" - haroldcarr
在bash / macOS 12.5上,我也需要引号(例如-name"*.swift")来像@haroldcarr所说那样进行递归遍历。 - ettore

6
我最终没有使用find命令,也没有创建备份文件。
sed -i '' 's/[[:space:]]*$//g' **/*.*

根据文件树的深度,这个(较短的版本)可能已经满足您的需求。

请注意,它也可以处理二进制文件。


针对特定文件:find . -name '.rb' | xargs -I{} sed -i '' 's/[[:space:]]$//g' {} - Gautam Rege
你在sed命令中不需要''参数;或者我可能漏掉了什么。我尝试在给定目录下的所有文件上执行此操作,如下所示:sed -i 's/[[:space:]]$//g' util/.m - Mircea

6

我使用正则表达式。4个步骤:

  1. 在编辑器中打开根文件夹(我使用Visual Studio Code)。
  2. 点击左侧的搜索图标,启用正则表达式模式。
  3. 在搜索栏输入“+\n”,在替换栏输入“\n”。
  4. 点击“全部替换”。

这将删除所有文件中每行末尾的空格,并且您可以排除一些不符合此要求的文件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接