【问题标题】:how to replace the tabs with empty space in each file of a directory如何在目录的每个文件中用空格替换选项卡
【发布时间】:2014-12-15 16:17:42
【问题描述】:

我想用对应的空白空间替换目录的每个文件中的选项卡。我已经找到了一个解决方案 11094383,您可以在其中将标签替换为 given 空格数:

> 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

有人可以帮忙吗?

【问题讨论】:

  • 你想用 6 个或 8 个空格替换制表符吗?
  • 在我的情况下,我必须用 4、6 或 8 个空格替换制表符
  • 你在找expandman.cx/expand
  • 它将用 8 个空格替换制表符。 find ./ -type f -exec sed -i 's/\t/\s\{8\}/g' {} \;
  • 请提供一些示例输入供我们使用。

标签: sed


【解决方案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 次换人处理:

  • 一个由 8 个非制表符组成的零个或多个序列的块,被捕获,然后是
  • 0-7 个非制表符的序列,也被捕获,然后是
  • 一个标签。
  • 它将匹配项替换为捕获的材料,后跟适当数量的空格。

在测试过程中发现的一个问题是,如果一行以空格结尾,pr 命令会删除该尾随空格。

在某些系统(至少是 BSD 或 Mac OS X)上还有 expand 命令,它保留了尾随空格。使用它比prsed 更简单。

使用这些sed 脚本,并使用带有备份文件的BSD 或GNU sed,您可以编写:

find . -type f -exec sed -i.bak -r -f script.sed {} +

(GNU sed 表示法;用 -E 代替 -r 代替 BSD sed。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-21
    • 2014-04-06
    • 2017-02-25
    相关资源
    最近更新 更多