如何使用 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 $_, ( $from - 1 ), ( $to - $from + 1 ) ) =~ tr/ /B/;' in_file

Perl 单行代码使用这些命令行标志:
-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(" ","B",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,strt,(end-strt));                       # Extract the 29th to the 39th characters of the line and read into variable ram
gsub(" ","B",ram);                                    # Replace spaces with B in ram
print substr($0,1,(strt-1)) ram 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;

另一个尴尬,但使用FS=""

$ awk 'BEGIN{FS=OFS=""}{for(i=29;i<=39;i++)sub(/ /,"B",$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(/ /,"B",$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})/143/
x
s/.*//
x' infile

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

sed -r 's/(.{236})(.{10})(.*)/1r2r3/;:a;s/(r.*) (.*r)/1B2/;ta;s/r//g' input

说明:
首先在要更改的区域周围放置r
Next 引入要跳回的标签。
Next 替换 2 个标记之间的空格。
重复直到替换所有空格。
删除标记。

在您的情况下,如果长度没有变化,您可以不使用标记.
替换 236..245 个字符后的空格,并在成功时重试。

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

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

sed -E 's/./&n/245;s//n&/236/;h;y/ /B/;H;g;s/n.*n(.*)n.*n(.*)n.*/21/' file

将问题分成 2 行,一条带有空格,另一行带有B有空格的地方。

然后使用模式匹配从两条线制作复合线。

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

最新更新