我想用对应的空格替换目录的每个文件中的选项卡。我找到了一个解决方案11094383,您可以用给定个空格数替换制表符:
> find ./ -type f -exec sed -i 's/\t/ /g' {} \;
在上面的解决方案中,标签被四个空格替换。但在我的情况下,标签可以占用更多的空间 - 例如8.
带有标签的文件示例,应该用8个空格替换:
NSMl1 100 PSHELL 0.00260 400000 400200 400300
400400 400500 400600 400700 400800 400900
401000 401100 400100 430000 430200 430300
430400 430500 430600 430700 430800 430900
431000 431100 430100 401200 431200
此处带有制表符的行是第3行到第5行。
带有标签的文件示例,应该用4个标签替换:
RBE2 1101001 5000511 123456 1100
有人可以帮忙吗?
答案 0 :(得分:1)
经典的答案是使用带有选项的pr
命令将标签扩展到适当数量的空格,转换分页功能:
pr -e8 -l1 -t …files…
棘手的部分是让文件覆盖,这似乎是问题的一部分。当然,GNU和BSD(Mac OS X)版本中的sed
支持使用-i
选项覆盖 - 两者之间存在变体行为,因为BSD sed
需要备份文件的后缀和GNU sed
没有。但是,sed
并不(很容易)支持将标签转换为适当数量的空白,因此它并不完全合适。
The UNIX Programming Environment中有一个脚本overwrite
(我缩写为ow
)可以做到这一点。我自1987年以来一直在使用该脚本(首次登记 - 最后一次更新于2005年)。
#!/bin/sh
# Overwrite file
# From: The UNIX Programming Environment by Kernighan and Pike
# Amended: remove PATH setting; handle file names with blanks.
case $# in
0|1) echo "Usage: $0 file command [arguments]" 1>&2
exit 1;;
esac
file="$1"
shift
new=${TMPDIR:-/tmp}/ovrwr.$$.1
old=${TMPDIR:-/tmp}/ovrwr.$$.2
trap "rm -f '$new' '$old' ; exit 1" 0 1 2 15
if "$@" >"$new"
then
cp "$file" "$old"
trap "" 1 2 15
cp "$new" "$file"
rm -f "$new" "$old"
trap 0
exit 0
else
echo "$0: $1 failed - $file unchanged" 1>&2
rm -f "$new" "$old"
trap 0
exit 1
fi
这些天在大多数系统上使用mktemp
命令是可能的,也可以说更好;它当时并不存在。
在问题的上下文中,您可以使用:
find . -type f -exec ow {} pr -e8 -t -l1 \;
您需要单独处理每个文件。
如果您真的决定使用sed
来完成工作,那么您的工作就会被删除。有一种可怕的方式来做到这一点。有一个符号问题;如何表示文字标签;我将使用\t
来表示它。该脚本将存储在一个文件中,我假设它是script.sed
:
:again
/^\(\([^\t]\{8\}\)*\)\t/s//\1 /
/^\(\([^\t]\{8\}\)*\)\([^\t]\{1\}\)\t/s//\1\3 /
/^\(\([^\t]\{8\}\)*\)\([^\t]\{2\}\)\t/s//\1\3 /
/^\(\([^\t]\{8\}\)*\)\([^\t]\{3\}\)\t/s//\1\3 /
/^\(\([^\t]\{8\}\)*\)\([^\t]\{4\}\)\t/s//\1\3 /
/^\(\([^\t]\{8\}\)*\)\([^\t]\{5\}\)\t/s//\1\3 /
/^\(\([^\t]\{8\}\)*\)\([^\t]\{6\}\)\t/s//\1\3 /
/^\(\([^\t]\{8\}\)*\)\([^\t]\{7\}\)\t/s//\1\3 /
t again
使用经典的sed
符号。
然后你可以写:
sed -f script.sed …data-files…
如果您有GNU sed
或BSD(Mac OS X)sed
,则可以使用扩展正则表达式:
:again
/^(([^\t]{8})*)\t/s//\1 /
/^(([^\t]{8})*)([^\t]{1})\t/s//\1\3 /
/^(([^\t]{8})*)([^\t]{2})\t/s//\1\3 /
/^(([^\t]{8})*)([^\t]{3})\t/s//\1\3 /
/^(([^\t]{8})*)([^\t]{4})\t/s//\1\3 /
/^(([^\t]{8})*)([^\t]{5})\t/s//\1\3 /
/^(([^\t]{8})*)([^\t]{6})\t/s//\1\3 /
/^(([^\t]{8})*)([^\t]{7})\t/s//\1\3 /
t again
然后运行:
sed -r -f script.sed …data-files… # GNU sed
sed -E -f script.sed …data-files… # BSD sed
脚本有什么作用?
第一行设置标签;如果中间的任何s///
操作进行了替换,则最后一行跳转到该标签。因此,对于文件的每一行,脚本循环直到没有匹配,因此不执行替换。
8次换人涉及:
测试期间发现的一个好奇心是,如果一行以空格结尾,pr
命令会删除该尾随空格。
在某些系统(至少是BSD或Mac OS X)上还有expand
命令,它保留了尾随的空白区域。使用它比pr
或sed
更简单。
使用这些sed
脚本,并将BSD或GNU sed
与备份文件一起使用,您可以写:
find . -type f -exec sed -i.bak -r -f script.sed {} +
(GNU sed
表示法;将-E
替换为-r
替换BSD sed
。)