如何将制表符转换为目录的每个文件中的空格(可能是递归的)?
另外,有没有办法设置每个标签的空格数?
警告:这会破坏你的回购。
这将损坏二进制文件,包括
svn
,.git
下的文件!使用前请阅读评论!
find . -type f -exec sed -i.orig 's/\t/ /g' {} +
原始文件保存为[filename].orig
。
缺点:
在找到混合制表符和空格后,我使用this post重新缩进所有的C / C ++代码。如果您愿意,它还可以选择强制特定的支撑样式。
我的建议是使用:
find . -type f -name *.java -exec bash -c 'expand -t 4 {} > /tmp/stuff;mv /tmp/stuff {}' \;
评论:
astyle
是一个流编辑器。使用find . -name '*.lua' -exec ex '+%s/\t/ /g' -cwq {} \;
进行就地编辑。这样可以避免为sed
中的每个替换创建额外的临时文件和产生shell。ex
而不是top answer。正如@ gniourf-gniourf所指出的,这会导致文件名中的空格,引号和控制字符出现问题。 find|xargs
。你可以使用find -exec
和Wheeler包。
首先,安装find
tabs-to-spaces
然后,从项目的根目录运行此命令;
tabs-to-spaces
这将在每个文件中用2个npm install -g tabs-to-spaces
替换每个find . -name '*' -exec t2s --spaces 2 {} \;
字符。
下载并运行以下脚本,以便将硬标签递归转换为纯文本文件中的软标签。
从包含纯文本文件的文件夹中执行脚本。
tab
人们可以使用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;
正如Carpetsmoker所说,它会根据你的vim
设置重新分配。和文件中的模型,如果有的话。此外,它不仅会在行的开头替换标签。这不是你通常想要的。例如,您可能有文字,包含标签。
在其他答案中建议使用find -type f \( -name '*.css' -o -name '*.html' -o -name '*.js' -o -name '*.php' \) -execdir vim -c retab -c wq {} \;
似乎是单独完成此任务的最合理的方法。
也就是说,它也可以用Bash和Awk完成,以防你可能想要做一些其他修改。
如果使用Bash 4.0或更高版本,可以使用vim
expand
以shopt builtin递归搜索。
使用GNU Awk 4.1或更高版本,可以进行“inplace”文件修改:
globstar
如果您想设置每个标签的空格数:
**
Git存储库友好的方法
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
仅对C或C ++文件起作用:
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"
)
你可能想要这个特别是因为那些需要制表符的恼人的Makefile。
命令git-tab-to-space
:
git-tab-to-space '\.(c|h)(|pp)$'
中没有任何内容如下所述:git grep --cached -Il ''
.git
保持文件权限不变:How to list all text (non-binary) files in a git repository?不幸的是我chmod --reference
。
如果您的代码库有疯狂的想法允许在字符串中使用功能原始选项卡,请使用:
https://unix.stackexchange.com/questions/20645/clone-ownership-and-permissions-from-another-file
然后一个接一个地浏览所有不开始的行标签,你可以列出:can't find a succinct POSIX alternative
在Ubuntu 18.04上测试过。
将标签转换为“.lua”文件中的空格[tabs - > 2个空格]
rpl
使用vim-way:
rpl -R -e "\t" " " .
find . -iname "*.lua" -exec sed -i "s#\t# #g" '{}' \;
。要修改tabstop,请添加$ ex +'bufdo retab' -cxa **/*.*
。
然而,不利的是它可以globstar
。
因此,对于更好的解决方案(通过使用替换),请尝试:
**
或者通过使用shopt -s globstar
编辑器+ **/*.c
实用程序:
+'set ts=2'
有关尾随空格,请参阅:replace tabs inside the strings
您可以在$ ex -s +'bufdo %s/^\t\+/ /ge' -cxa **/*.*
中添加以下功能:
ex
简单地用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
包,并避免clearing the input file。最后,在使用Homebrew(gexpand
)安装coreutils
之后,您可以在OSX上使用brew install coreutils
。
收集Gene's answer的最佳评论,迄今为止最好的解决方案是使用来自sponge
的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"
)在这里不起作用,因为it would overwrite the file too soon。
优点:保留所有原始文件权限,并且不使用中间tmp
文件。
使用反斜杠转义的sed
。
在linux上:
sed -i $'s/\t/-/g' *.txt
sed -i $'s/\t/ /g' *.txt
sed -i $'s/\t/ /g' *.txt
在mac上:
sed -i '' $'s/\t/ /g' *.txt
如何将制表符转换为目录的每个文件中的空格(可能是递归的)?
这通常不是你想要的。
你想为png图像做这个吗? PDF文件? .git目录?你的Makefile
(需要标签)?一个5GB的SQL转储?
理论上,你可以将大量的排除选项传递给find
或其他任何你正在使用的东西;但这很脆弱,一旦添加其他二进制文件就会中断。
你想要的,至少是:
expand
这样做,sed
没有)。据我所知,没有“标准”的Unix实用程序可以做到这一点,并且使用shell一行代码并不是很容易,因此需要一个脚本。
不久之前,我创建了一个名为sanitize_files的小脚本。它还修复了一些其他常见的东西,比如用\r\n
替换\n
,添加一个尾随的\n
等。
您可以在下面找到没有额外功能和命令行参数的简化脚本,但我建议您使用上面的脚本,因为它更有可能收到错误修正和其他更新的帖子。
我还想指出,在回答这里的其他一些答案时,使用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)
我喜欢上面的“查找”示例,用于递归应用程序。为了使其适应非递归,只改变当前目录中与通配符匹配的文件,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
您可以使用通常可用的pr
命令(手册页here)。例如,要将制表符转换为四个空格,请执行以下操作:
pr -t -e=4 file > file.expanded
-t
抑制标题要以递归方式转换目录树中的所有文件,同时跳过二进制文件:
-e=num
跳过二进制文件的逻辑来自num
。
注意:
要在目录中递归转换所有Java文件以使用4个空格而不是选项卡:
#!/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