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 / 问题 / 570101
Accepted
Tagwint
Tagwint
Asked: 2020-02-28 08:52:33 +0800 CST2020-02-28 08:52:33 +0800 CST 2020-02-28 08:52:33 +0800 CST

修改由行号指定的非相邻行

  • 772

我事先知道行号,并将它们保存在另一个文件中:

cat linenos
2
15
42
44
... etc

如您所见,这些行不相邻,因此我不能将一个范围用于sed. 目标是修改目标文件行,例如,在它们前面加上像 MARKER 这样的标记

直截了当的方法是sed多次调用来修改每一行:

for l in $(cat linenos)
do 
  sed -i "${l}s/^/MARKER/" target_file
done

这显然会多次调用 sed 。

注意:*这种方法不仅效率低下,而且如果修改不是像这样插入标记,它也会使事情出错。任何行删除或插入 sed 命令(如 dar)都会使 linenos 中的初始行号对于循环中的下一个 sed 运行无效。

你有什么建议来改进/优化它?

示例 linenos 文件

cat linenos
2
5

示例目标文件

cat target_file
line one
line two
line three
line four
line five
line six

修改后的 target_file 的预期结果

cat target_file
line one
MARKERline two
line three
line four
MARKERline five
line six

我想出的可能方法是动态创建 sed 场景

SEDCMD=$(for l in $(cat linenos); do echo -n "${l}s/^/MARK/;" ; done)

sed -i -e "$SEDCMD" targetfile

@steeldriver 的以下方法分享了这个想法,但更加优雅和简洁

bash sed
  • 5 5 个回答
  • 433 Views

5 个回答

  • Voted
  1. steeldriver
    2020-02-28T09:31:41+08:002020-02-28T09:31:41+08:00

    -f您可以使用 sed 本身(或您选择的其他文本处理实用程序)将行号转换为 sed 表达式,然后使用开关将它们传递给 sed

    前任。

    sed 's:$:s/^/MARKER/:' linenos | sed -f- -i target_file
    

    这至少只调用 sed两次。

    • 10
  2. Stéphane Chazelas
    2020-02-28T11:20:00+08:002020-02-28T11:20:00+08:00

    使用perl(GNU的sed来源-i):

    perl -pi -e '
      BEGIN{$l{0+$_}=1 while <STDIN>}
      $_ = "MARKER$_" if $l{$.}' target_file < linenos
    

    我们在 的标准输入上提供行号列表perl。BEGIN这是在块中读取的。

    对于每一行输入,我们将该行转换为带有 的数字0+$_。这使得换行符消失并且还规范化了数字(所有 1e0、1、01 变为 1)。

    %l散列表填充有每个行号的值作为1键。

    将target_file在主-p循环中处理,其中MARKERS添加到当前行号 ( $.)所在行%l的非零值。

    • 6
  3. Ed Morton
    2020-02-28T13:44:46+08:002020-02-28T13:44:46+08:00
    $ awk 'NR==FNR{a[$1]="MARKER"; next} {print a[FNR] $0}' linenos target_file
    line one
    MARKERline two
    line three
    line four
    MARKERline five
    line six
    

    或节省一点内存:

    $ awk 'NR==FNR{a[$1]; next} {print (FNR in a ? "MARKER" : "") $0}' linenos target_file
    line one
    MARKERline two
    line three
    line four
    MARKERline five
    line six
    

    如果您想要“就地”编辑(与 perl 和 GNU sed 相同-i),请使用 GNU awk 并在语句之前awk '...'更改awk -i inplace '...'并添加一个,这样您的文件就不会被清空。恕我直言,使用任何 awk(或任何其他 UNIX 工具)执行此操作更简单:print;nextlinenos

    awk 'script' linenos target_file > tmp && mv tmp target_file
    
    • 4
  4. Best Answer
    user232326
    2020-03-01T11:23:56+08:002020-03-01T11:23:56+08:00

    iffileN包含要修改的行数,andtarget_file是包含要修改的行的文本文件。最低限度的解决方案将需要读取每个文件一次。

    已排序

    如果包含行号的文件每行包含一个数字(大于 1),已排序并且没有重复,我们可以使用:

    awk 'BEGIN{ getline lineN <"fileN"} {
         if(NR==lineN){$0="MARKER " $0;getline lineN <"fileN"}
         }1' target_file
    

    这将只在内存中保留一行(每个文件)并从头到尾遍历两个文件。但是,一旦 awk 处理了一行,例如第 15 行,它就不会返回到第 12 行。因此,必须对文件lineN进行排序(不重复,并且大于 1)才能正常工作。

    未分类

    当然,天真的解决方案是可以对行号文件进行排序sort -nu fileN。

    但是,如果行号列表可能未排序(并且重复),我们可以使用 sed ed( 的前身sed)或 awk (稍后):

    将每一行转换lineN为 sed 编辑命令,例如s/^/MARKER /. shell printf 或 sed 都可以做到这一点:

    printf '%ss/^/MARKER /\n' $(<fileN) | sed -f - target_file
    sed 's#$#s/^/MARKER /#' fileN       | sed -f - target_file
    
    { printf '%ss/^/MARKER /\n' $(<fileN); printf '%s\n' ,p Q; } | ed -Gs target_file
    { sed 's#$#s/^/MARKER /#' fileN ; echo "w"      ; } | ed target_file
    

    请注意,在最后一种情况下,编辑是直接在原始文件中完成的。最后一个命令w将修改写入文件。如果需要打印结果,则使用第三个选项,它将打印所有行。

    awk

    fileN在awk中,在内存和进程中捕获整体target_file

    awk '{ if(NR==FNR){
                         a[$1]=1
                      }else{
                         if(a[FNR]==1){ printf("%s","MARKER ")};
                         print 
                      }
         }' fileN target_file
    

    或者,使用一个变量来控制带有行号的文件列表何时结束:

    awk '{ if (dofile==1) {   if(a[FNR]==1){ printf("%s","MARKER ")};
                              print
                          }else{
                              a[$1]=1
                          }
         }' fileN fileK   dofile=1   target_file
    

    请注意,最后一个版本允许多个带有行号的文件,例如示例中的fileN和fileK。

    另请注意,awk 版本不处理重复的行号。所有重复的行号只处理一次。

    • 2
  5. Shawn
    2020-02-28T14:02:36+08:002020-02-28T14:02:36+08:00

    另一种方式,使用ed而不是sed修改target_file就地:

    (while IFS= read n; do echo "${n}s/^/MARKER/"; done < linenos; echo w) |  ed -s target_file
    
    • 1

相关问题

  • 通过命令的标准输出以编程方式导出环境变量[重复]

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

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

  • `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