如何使用 sed 或 awk 等命令行实用程序替换已知开始和停止位置之间的文件中的文本?

问题描述

我一直在修补这个问题,但无法完全弄清楚。文件中的示例行如下所示:

"...~236 characters of data...Y  YYY.  Y...many more characters of data"

我将如何使用 sed 或 awk 仅在位置 236 和 246 之间用 B 字符替换空格?在该示例字符串中,它从字符串中的第 29 个字符开始,到第 39 个字符结束。我想保留行内目标数据块前后的所有文本。

为了根据注释进行澄清,它应该应用于文件中的所有行,预期输出为:

"...~236 characters of data...YBBYYY.BBY...many more characters of data"

解决方法

使用GNU awk

$ awk -v FIELDWIDTHS='29 10 *' -v OFS= '{gsub(/ /,"B",$2)} 1' ip.txt
...~236 characters of data...YBBYYY.BBY...many more characters of data

FIELDWIDTHS='29 10 *' 表示第一个字段的 29 个字符,第二个字段的下 10 个字符,第三个字段的其余字符。 OFS 设置为空,否则会在字段之间添加空格。

使用perl

$ perl -pe 's/^.{29}\K.{10}/$&=~tr| |B|r/e' ip.txt
...~236 characters of data...YBBYYY.BBY...many more characters of data
  • ^.{29}\K 匹配并忽略前 29 个字符
  • .{10} 匹配 10 个字符
  • e 标志允许替换部分中的 Perl 代码而不是字符串
  • $&=~tr| |B|r 将匹配部分的空格转换为 B
,

将此 Perl 单行代码与 substrtr 一起使用。请注意,这使用了您可以分配给 substr 的事实,这会更改原始字符串:

perl -lpe 'BEGIN { $from = 29; $to = 39; } (substr $_,( $from - 1 ),( $to - $from + 1 ) ) =~ tr/ /B/;' in_file > out_file

要就地更改文件,请使用:

perl -i.bak -lpe 'BEGIN { $from = 29; $to = 39; } (substr $_,( $to - $from + 1 ) ) =~ tr/ /B/;' in_file

Perl one-liner 使用这些命令行标志:
-e :告诉 Perl 查找内嵌代码,而不是在文件中。
-p :一次循环输入一行,默认情况下将其分配给 $_。在每次循环迭代后添加 print $_
-l : 在执行内联代码之前去除输入行分隔符(默认情况下在 *NIX 上为 "\n"),并在打印时附加它。
-i.bak :就地编辑输入文件(覆盖输入文件)。在覆盖之前,通过在其名称后附加扩展名 .bak 来保存原始文件的备份副本。

,

我会按照以下方式使用 GNU AWK,为简单起见,假设我们有 file.txt 内容

S o m e s t r i n g

并且想要将空格从 5(含)更改为 10(含)位置然后

awk 'BEGIN{FPAT=".";OFS=""}{for(i=5;i<=10;i+=1)$i=($i==" "?"B":$i);print}' file.txt

输出是

S o mBeBsBt r i n g

说明:我将字段模式 (FPAT) 设置为任何单个字符,并将输出字段分隔符 (OFS) 设置为空字符串,因此每个字段都由单个字符填充,并且我没有得到多余的空间当 print-ing。我使用 for 循环访问所需的字段,并检查每个字段是否为空格,如果是,则在此处分配 B 否则我分配原始值,最后我 print 整个更改行.

,

使用 GNU awk:

awk -v strt=29 -v end=39 '{ ram=substr($0,strt,(end-strt));gsub(" ",ram);print substr($0,1,(strt-1)) ram substr($0,(end)) }' file

说明:

awk -v strt=29 -v end=39 '{                                                          # Pass the start and end character positions as strt and end respectively
                               ram=substr($0,(end-strt));                       # Extract the 29th to the 39th characters of the line and read into variable ram
                               gsub(" ",ram);                                    # Replace spaces with B in ram
                               print substr($0,(end))      # Rebuild the line incorporating raw and printing the result
                           }'file
,

对于perl来说,这当然是一项合适的任务,而且我的perl已经变得如此生锈,以至于这是我目前能想到的最好的方法,这让我感到难过:

perl -e 'local $/=\1;while(<>) { s/ /B/ if $. >= 236 && $. <= 246; print }' input;
,

另一个 awk,但使用 FS=""

$ awk 'BEGIN{FS=OFS=""}{for(i=29;i<=39;i++)sub(/ /,$i)}1' file

输出:

"...~236 characters of data...YBBYYY.BBY...many more characters of data"

说明:

$ awk '                    # yes awk yes
BEGIN {
    FS=OFS=""              # set empty field delimiters
}
{
    for(i=29;i<=39;i++)    # between desired indexes
        sub(/ /,$i)    # replace space with B
        # if($i==" ")      # couldve taken this route,too 
        #     $i="B"  
}1' file                   # implicit output
,

使用 sed :

sed '
H
s/\(.\{236\}\)\(.\{11\}\).*/\2/
s/ /B/g
H
g
s/\n//g
s/\(.\{236\}\)\(.\{11\}\)\(.*\)\(.\{11\}\)/\1\4\3/
x
s/.*//
x' infile
,

当你有一个没有 \r 的输入字符串时,你可以使用:

sed -r 's/(.{236})(.{10})(.*)/\1\r\2\r\3/;:a;s/(\r.*) (.*\r)/\1B\2/;ta;s/\r//g' input

说明:
首先将 \r 放在要更改的区域周围。
接下来介绍一个标签跳转回。
接下来替换 2 个标记之间的空格。
重复直到所有空格都被替换。
删除标记。

在您的情况下,长度不变,您可以不用标记。
236..245个字符后替换一个空格,成功后再试。

sed -r ':a; s/^(.{236})([^ ]{0,9}) /\1\2B/;ta' input
,

这可能对你有用(GNU sed):

results

将问题分成两行,一行带有空格,另一行带有 sed -E 's/./&\n/245;s//\n&/236/;h;y/ /B/;H;g;s/\n.*\n(.*)\n.*\n(.*)\n.*/\2\1/' file 的位置。

然后使用模式匹配将两行组合成一条线。

注意换行符可以用作分隔符,因为它保证不在 seds 模式空间中。

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...