如何将制表符转换为目录的每个文件中的空格(可能是递归的)?
另外,是否可以设置每个标签的空格数?
pr
是一个很棒的工具。看到这个答案。
如何将制表符转换为目录的每个文件中的空格(可能是递归的)?
另外,是否可以设置每个标签的空格数?
pr
是一个很棒的工具。看到这个答案。
Answers:
警告:这将破坏您的回购。
这将损坏的二进制文件,包括那些在
svn
,.git
!使用前请阅读评论!
find . -iname '*.java' -type f -exec sed -i.orig 's/\t/ /g' {} +
原始文件另存为[filename].orig
。
将“ * .java”替换为您要查找的文件类型的文件结尾。这样,您可以防止二进制文件的意外损坏。
缺点:
find ./ -type f -exec sed -i 's/^\t/####/g' {} \;
。但是我不知道expand命令-非常有用!
简单替换sed
是可以的,但不是最好的解决方案。如果选项卡之间存在“多余”空格,则替换后它们仍将存在,因此页边空白将变得参差不齐。行中间扩展的选项卡也将无法正常工作。在中bash
,我们可以改为
find . -name '*.java' ! -type d -exec bash -c 'expand -t 4 "$0" > /tmp/e && mv /tmp/e "$0"' {} \;
应用于expand
当前目录树中的每个Java文件。-name
如果要定位其他文件类型,请删除/替换参数。正如评论中提到的那样,在删除-name
或使用弱通配符时要非常小心。您可以无意间轻易破坏存储库和其他隐藏文件。这就是为什么原始答案包括以下内容的原因:
在尝试类似的操作之前,您应该始终制作树的备份副本,以防出现问题。
{}
。看起来他不知道$0
何时-c
使用。然后dimo414从我在转换目录中使用temp更改为/tmp
,如果/tmp
在不同的挂载点上,它将慢得多。不幸的是,我没有Linux盒子可以测试您的$0
建议。但是我认为你是对的。
find . -name '*.java' ! -type d -exec bash -c 'expand -t 4 "$0" > /tmp/e && mv /tmp/e "$0"' {} \;
sponge
从joeyh.name/code/moreutils,你可以写find . -name '*.py' ! -type d -exec bash -c 'expand -t 8 "$0" | sponge "$0"' {} \;
find . -name '*'
,我只是销毁了我本地的git repo
试试命令行工具expand
。
expand -i -t 4 input | sponge output
哪里
最后,gexpand
在coreutils
通过Homebrew(brew install coreutils
)安装后,可以在OSX上使用。
-i
转到expand
以仅替换每行上的前导标签。这有助于避免替换可能是代码一部分的选项卡。
input
相同,则文件output
甚至在启动前就破坏了内容expand
。这是如何>
工作的。
从收集的最好注解基因的答案,目前最好的解决办法,是通过使用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
是不区分大小写的匹配项(对于“ likes” *.java
和“ *.JAVA
likes”)type -f
仅查找常规文件(无目录,二进制文件或符号链接)-exec bash -c
在子外壳中为每个文件名执行以下命令, {}
expand -t 4
将所有TAB扩展到4个空格sponge
吸收标准输入(来自expand
)并写入文件(相同)*。注意:*一个简单的文件重定向(> "$0"
)在这里不起作用,因为它会很快覆盖文件。
优点:保留所有原始文件权限,并且不tmp
使用任何中间文件。
使用反斜杠转义的sed
。
在Linux上:
在所有* .txt文件中,将所有标签替换为1个连字符:
sed -i $'s/\t/-/g' *.txt
在所有* .txt文件中,将所有标签替换为1个空格:
sed -i $'s/\t/ /g' *.txt
在所有* .txt文件中,将所有标签替换为4个空格:
sed -i $'s/\t/ /g' *.txt
在Mac上:
在所有* .txt文件中,将所有标签替换为4个空格:
sed -i '' $'s/\t/ /g' *.txt
sed -i '' $'s/\t/ /g' $(find . -name "*.txt")
您可以使用一般可用的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
跳过二进制文件的逻辑来自于这篇文章。
注意:
expand
鉴于两者都是POSIX,有什么优势吗?例如,它有内联更改选项吗?Git安全性:stackoverflow.com/a/52136507/895245
如何将制表符转换为目录的每个文件中的空格(可能是递归的)?
这通常不是您想要的。
您要对png图像执行此操作吗?PDF文件?.git目录?您的
Makefile
(需要标签)?5GB的SQL转储?
从理论上讲,您可以将大量排除选项传递给find
或您使用的任何其他选项;但这很脆弱,一旦添加其他二进制文件,它就会崩溃。
您想要的至少是:
expand
这样做,sed
不是)。据我所知,没有“标准的” Unix实用程序可以做到这一点,而且使用shell一线式处理不是很容易,因此需要一个脚本。
不久前,我创建了一个名为sanitize_files的小脚本
,它确实可以做到这一点。它还修复了一些其他常见的问题,例如替换\r\n
为\n
,添加结尾\n
等。
您可以在下面找到一个没有附加功能和命令行参数的简化脚本,但我建议您使用上面的脚本,因为与本文相比,它更可能收到错误修复和其他更新。
我还想指出,针对此处的其他一些答案,使用shell globbing 并不是一种健壮的方法,因为迟早您最终会得到比适合的文件更多的文件ARG_MAX
(在现代在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
ARG_MAX
长度。在Linux系统上,这是128k,但是我已经遇到了这个限制足够的时间,以至于不依赖于外壳程序。
find
可以知道-maxdepth 1
,它仅处理要修改的目录的条目,而不处理整个树。
一个可以vim
用于:
find -type f \( -name '*.css' -o -name '*.html' -o -name '*.js' -o -name '*.php' \) -execdir vim -c retab -c wq {} \;
如Carpetsmoker所述,它将根据您的vim
设置重新制表。并在文件中进行模式设置(如果有)。此外,它不仅将替换行首的制表符。这不是您通常想要的。例如,您可能有包含标签的文字。
:retab
将更改文件中的所有标签,而不是开头的标签。这也取决于你的是什么:tabstop
,并:expandtab
设置在vimrc和模式行,所以这可能不是在所有的工作。
tabstop
和expandtab
设置,如果您使用,它将可以解决vim
。除非文件中有模式行。
我的建议是使用:
find . -name '*.lua' -exec ex '+%s/\t/ /g' -cwq {} \;
注释:
ex
可能并非在每个Unix系统上都可用。将其替换为vi -e
可能在更多计算机上工作。同样,您的正则表达式用两个空格替换任意数量的起始制表符。将regex替换+%s/\t/ /g
为不会破坏多级缩进。但是,这也会影响未用于缩进的制表符。
/\t/ /
在文件上使用了变体,但是选择了/\t\+//
不破坏非缩进选项卡。错过了多缩进的问题!更新答案。[1] man7.org/linux/man-pages/man1/ex.1p.html#SEE%C2%A0ALSO
xargs
以这种方式使用是无用的,效率低下的和损坏的(想想包含空格或引号的文件名)。为什么不使用find
' -exec
开关呢?
-print0
查找/ xargs的选项。我之所以喜欢xargs,是-exec
因为:a)关注点分离b)它可以更轻松地与GNU parallel交换。
要以递归方式将目录中的所有Java文件转换为使用4个空格而不是制表符:
find . -type f -name *.java -exec bash -c 'expand -t 4 {} > /tmp/stuff;mv /tmp/stuff {}' \;
您可以使用find
与tabs-to-spaces
包这一点。
一,安装 tabs-to-spaces
npm install -g tabs-to-spaces
然后,从项目的根目录运行此命令;
find . -name '*' -exec t2s --spaces 2 {} \;
这会将每个文件中的每个tab
字符替换为2 spaces
。
没有人提到rpl
?使用rpl可以替换任何字符串。要将制表符转换为空格,
rpl -R -e "\t" " " .
很简单。
expand
仅在完成此任务时,使用其他答案中建议的方法似乎是最合乎逻辑的方法。
也就是说,如果您可能希望同时进行其他一些修改,也可以使用Bash和Awk来完成。
如果使用的是Bash 4.0或更高版本,则内置shopt globstar
可用于通过进行递归搜索**
。
使用GNU Awk 4.1或更高版本,可以对sed进行“就地”文件修改:
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
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上测试。
仅在“ .lua”文件中将制表符转换为空格[制表符-> 2个空格]
find . -iname "*.lua" -exec sed -i "s#\t# #g" '{}' \;
expand -t 4 input >output
)的功能相同
expand -t 4
会将标签页扩展a\tb
为3个空格,并将标签页扩展aa\tb
为2个空格。expand
考虑制表符的上下文,sed
不考虑制表符,并且无论上下文如何,都将使用您指定的空格量替换制表符。
使用vim-way:
$ ex +'bufdo retab' -cxa **/*.*
globstar
(**
)递归,请通过激活shopt -s globstar
。**/*.c
。要修改制表符,请添加+'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 $*
}
:retab
可能根本不起作用,shell globbing对于这种情况是一个不好的解决方案,您的:s
命令将用2个空格替换任意数量的制表符(您几乎永不想要),仅从运行:!expand
过程开始