AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 702347
Accepted
Roel Van de Paar
Roel Van de Paar
Asked: 2022-05-13 20:24:15 +0800 CST2022-05-13 20:24:15 +0800 CST 2022-05-13 20:24:15 +0800 CST

Bash:sed 或 awk 重写数字序列

  • 772

如何编写将重写以下内容的sed(或awk,或两者):

echo 'v100 v201 v102 v300 v301 v500 v999 v301' | sed/awk ...

到这个输出:

v1 v2 v3 v4 v5 v6 v7 v5

即每个后续vx都被重写以开始,v1...vn并且在v序列中使用相同的位置(即v301)v应该应用相同的(如中v5)。

旁注:示例输入序列显示了所有可能的可能性(即重复、无序的原始数据、原始数字的跳跃)。

您是可以回答这个问题的 sed 或 awk 专家吗?

bash awk
  • 5 5 个回答
  • 348 Views

5 个回答

  • Voted
  1. Stéphane Chazelas
    2022-05-13T23:30:23+08:002022-05-13T23:30:23+08:00

    与perl:

    $ echo 'v100 v201 v102 v300 v301 v500 v999 v301' |
       perl -pe 's{v\K\d+}{$seen{$&} //= ++$n}ge'
    v1 v2 v3 v4 v5 v6 v7 v5
    
    • v\d+匹配v后跟一个或多个十进制数字。\Kafterv重置匹配部分的开头,保留K其左侧的内容, the v,以便仅替换数字序列s。
    • 该e标志导致替换被视为被评估以产生替换的代码。e在该代码中,$&包含匹配的部分。
    • A // B是OR的一种形式,扩展为AifA已定义,B否则(与之相反A || B,扩展为AifA解析为真值,B否则)。//=是对应的赋值形式。所以A //= B是if (defined(A)) {A} else {A = B}.

    请注意,$seen哈希表在这些数字的字符串值上建立索引,依此类推v2 v02 v002,您将得到v1 v2 v3,2并且02是002彼此不同的字符串。您可以替换$&为0+$&规范化数字(010 被视为 10,而不是八进制 8),以便获得v1 v1 v1上面的示例。或者,您可以s{v0*\K\d+}{$seen{$&} //= ++$n}ge保留前导0s 并v1 v01 v001改为 get 。

    例如,为了避免替换v1找到的,您可以在匹配 ( )的两侧rev1sion添加一些单词boundary 正则表达式运算符。\bv\K\d+\b或者仅替换以空格分隔的单词(v1.2例如不理会),为非白色步调添加一些负面的环顾四周: .S(?<!\S)v\K\d+(?!\S)

    • 5
  2. Best Answer
    Kusalananda
    2022-05-13T23:59:28+08:002022-05-13T23:59:28+08:00

    使用awk:

    awk '{ for (i=1; i<=NF; ++i) $i = (seen[$i] ? seen[$i] : seen[$i] = "v" ++n) }; 1'
    

    这将遍历每个输入行的所有字段并重新分配它。重新分配的值v后面是 counter 的下一个值n,除非该字段的值以前曾见过,在这种情况下,它的新值将与之前给出的该字段的值相同。

    最后1的 触发修改行的输出。

    测试:

    $ echo 'v100 v201 v102 v300 v301 v500 v999 v301' | awk '{ for (i=1; i<=NF; ++i) $i = (seen[$i] ? seen[$i] : seen[$i] = "v" ++n) }; 1'
    v1 v2 v3 v4 v5 v6 v7 v5
    

    awk仅在与正则表达式匹配时修改字段的替代命令^v[0-9]+$:

    awk '{ for (i=1; i<=NF; ++i) if ($i ~ "^v[0-9]+$") $i = (seen[$i] ? seen[$i] : seen[$i] = "v" ++n) }; 1'
    

    或者,跨多行格式化以提高可读性:

    awk '
    {
        for (i=1; i<=NF; ++i)
            if ($i ~ "^v[0-9]+$")
                $i = (seen[$i] ? seen[$i] : seen[$i] = "v" ++n)
    }; 1'
    
    • 5
  3. Stéphane Chazelas
    2022-05-14T05:27:35+08:002022-05-14T05:27:35+08:00

    GNU 实现awk支持RS被定义为正则表达式,并在RT特殊变量中记录它匹配的内容。因此,使用它,您可以执行以下操作:

    $ echo 'v100 v201 v102 v300 v301 v500 v999 v301' |
       gawk -v RS='v[0-9]+' -v ORS= '
         RT {$0 = $0 "v" (RT in seen ? seen[RT] : seen[RT] = ++n)}1'
    v1 v2 v3 v4 v5 v6 v7 v5
    

    请注意,它会替换所有出现的v后跟数字,即使是在诸如 inrev1.2或之类的单词中找到的那些rev0lution。就像我的 perl 方法一样,如果数字可能是零填充的,您可能需要调整它。

    • 2
  4. cas
    2022-05-13T22:05:18+08:002022-05-13T22:05:18+08:00

    如果您的输入仅包含后跟数字的“v”字符串,并且您可以使用空格分隔的输出,则此 perl 脚本可以执行您想要的操作:

    $ echo 'v100 v201 v102 v300 v301 v500 v999 v301' | 
        perl -lne '
          @line = ();
          #my $i=0;
          #my %seen=();
          while (/(v\d+)/g) {
            $seen{$1} = "v" . ++$i unless ($seen{$1});
            push @line, $seen{$1}
          };
          print join(" ",@line);'
    v1 v2 v3 v4 v5 v6 v7 v5
    

    perl 的-n选项遍历输入的每一行(类似于sed -n脚本),并-l自动从输入行的末尾截取换行符并将它们添加回打印语句。

    该while (/(v\d+)/g) 循环迭代(并捕获)每个输入行中$1匹配的所有字符串。v\d+如果该匹配项之前没有出现过,则增加计数器并将其添加到 %seen 哈希中。然后push(即添加到末尾)一个名为@line. 在while 循环结束后(即处理完输入行之后),打印@line 数组,每个元素之间有一个空格字符。

    对于每个输入行,@line 数组都重置为空。如果您还希望为每个输入行重置编号 ( $i) 和哈希,请取消注释该行之前的两行:%seenwhile(...)

    my $i=0;
    my %seen=();
    
    • 1
  5. nezabudka
    2022-05-14T06:31:40+08:002022-05-14T06:31:40+08:00

    仅限 GNU awk:

    echo 'v100 v201 v102 v300 v301 v500 v999 v301' |
      awk -v RS='[[:space:]]' -F '' '
        $0 {printf "%s", $1 (A[$0]?A[$0]:A[$0]=++i) RT}'
    v1 v2 v3 v4 v5 v6 v7 v5
    
    • 0

相关问题

  • 多行文件洗牌

  • 从文本文件传递变量的奇怪问题

  • 虽然行读取保持转义空间?

  • `tee` 和 `bash` 进程替换顺序

  • 运行一个非常慢的脚本直到它成功

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    模块 i915 可能缺少固件 /lib/firmware/i915/*

    • 3 个回答
  • Marko Smith

    无法获取 jessie backports 存储库

    • 4 个回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    user12345 无法获取 jessie backports 存储库 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl 为什么大多数 systemd 示例都包含 WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve