如何使用 sed 或 awk 等命令行实用程序替换文件中已知开始和停止位置之间的文本?
How to replace text in file between known start and stop positions with a command line utility like sed or 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 strt=29 -v end=39 '{ ram=substr([=10=],strt,(end-strt));gsub(" ","B",ram);print substr([=10=],1,(strt-1)) ram substr([=10=],(end)) }' file
解释:
awk -v strt=29 -v end=39 '{ # Pass the start and end character positions as strt and end respectively
ram=substr([=11=],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([=11=],1,(strt-1)) ram substr([=11=],(end)) # Rebuild the line incorporating raw and printing the result
}'file
这当然是适合 perl
的任务,令我难过的是我的 perl
已经生锈了,这是我目前能想到的最好的:
perl -e 'local $/=;while(<>) { s/ /B/ if $. >= 236 && $. <= 246; print }' input;
将此 Perl 单行代码与 substr
and tr
结合使用。请注意,这使用了可以分配给 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
想把spaces从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 时多余的 space。我使用 for
循环来访问所需的字段,对于每个我检查它是否是 space,如果是我在这里分配 B
否则我分配原始值,最后我 print
整行更改。
另一个 awk 但使用 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
与GNU awk
:
$ awk -v FIELDWIDTHS='29 10 *' -v OFS= '{gsub(/ /, "B", )} 1' ip.txt
...~236 characters of data...YBBYYY.BBY...many more characters of data
FIELDWIDTHS='29 10 *'
表示第一个字段 29
个字符,第二个字段接下来的 10 个字符,第三个字段的其余字符。 OFS
设置为空,否则您将在字段之间添加 space。
与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
将匹配部分 space 转换为 B
使用 sed :
sed '
H
s/\(.\{236\}\)\(.\{11\}\).*//
s/ /B/g
H
g
s/\n//g
s/\(.\{236\}\)\(.\{11\}\)\(.*\)\(.\{11\}\)//
x
s/.*//
x' infile
当你有一个没有\r
的输入字符串时,你可以使用:
sed -r 's/(.{236})(.{10})(.*)/\r\r/;:a;s/(\r.*) (.*\r)/B/;ta;s/\r//g' input
解释:
首先在要更改的区域周围放置\r
。
接下来介绍一个跳转标签。
接下来替换 2 个标记之间的 space。
重复直到所有 space 被替换。
删除标记。
在你的情况下,长度不变,你可以不用标记。
在236..245个字符后替换一个space,成功后再试
sed -r ':a; s/^(.{236})([^ ]{0,9}) /B/;ta' input
这可能适合您 (GNU sed):
sed -E 's/./&\n/245;s//\n&/236/;h;y/ /B/;H;g;s/\n.*\n(.*)\n.*\n(.*)\n.*//' file
将问题分成 2 行,一行包含 space,另一行包含 B
,其中有 space。
然后使用模式匹配将两条线合成一条线。
N.B。换行符可以用作分隔符,因为它保证不会出现在 seds 模式 space.
中
我已经对此进行了一段时间的修改,但还是不太明白。文件中的示例行如下所示:
"...~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 strt=29 -v end=39 '{ ram=substr([=10=],strt,(end-strt));gsub(" ","B",ram);print substr([=10=],1,(strt-1)) ram substr([=10=],(end)) }' file
解释:
awk -v strt=29 -v end=39 '{ # Pass the start and end character positions as strt and end respectively
ram=substr([=11=],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([=11=],1,(strt-1)) ram substr([=11=],(end)) # Rebuild the line incorporating raw and printing the result
}'file
这当然是适合 perl
的任务,令我难过的是我的 perl
已经生锈了,这是我目前能想到的最好的:
perl -e 'local $/=;while(<>) { s/ /B/ if $. >= 236 && $. <= 246; print }' input;
将此 Perl 单行代码与 substr
and tr
结合使用。请注意,这使用了可以分配给 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
想把spaces从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 时多余的 space。我使用 for
循环来访问所需的字段,对于每个我检查它是否是 space,如果是我在这里分配 B
否则我分配原始值,最后我 print
整行更改。
另一个 awk 但使用 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
与GNU awk
:
$ awk -v FIELDWIDTHS='29 10 *' -v OFS= '{gsub(/ /, "B", )} 1' ip.txt
...~236 characters of data...YBBYYY.BBY...many more characters of data
FIELDWIDTHS='29 10 *'
表示第一个字段 29
个字符,第二个字段接下来的 10 个字符,第三个字段的其余字符。 OFS
设置为空,否则您将在字段之间添加 space。
与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
将匹配部分 space 转换为B
使用 sed :
sed '
H
s/\(.\{236\}\)\(.\{11\}\).*//
s/ /B/g
H
g
s/\n//g
s/\(.\{236\}\)\(.\{11\}\)\(.*\)\(.\{11\}\)//
x
s/.*//
x' infile
当你有一个没有\r
的输入字符串时,你可以使用:
sed -r 's/(.{236})(.{10})(.*)/\r\r/;:a;s/(\r.*) (.*\r)/B/;ta;s/\r//g' input
解释:
首先在要更改的区域周围放置\r
。
接下来介绍一个跳转标签。
接下来替换 2 个标记之间的 space。
重复直到所有 space 被替换。
删除标记。
在你的情况下,长度不变,你可以不用标记。
在236..245个字符后替换一个space,成功后再试
sed -r ':a; s/^(.{236})([^ ]{0,9}) /B/;ta' input
这可能适合您 (GNU sed):
sed -E 's/./&\n/245;s//\n&/236/;h;y/ /B/;H;g;s/\n.*\n(.*)\n.*\n(.*)\n.*//' file
将问题分成 2 行,一行包含 space,另一行包含 B
,其中有 space。
然后使用模式匹配将两条线合成一条线。
N.B。换行符可以用作分隔符,因为它保证不会出现在 seds 模式 space.
中