使用自定义实用程序进行搜索和替换以执行转换



我想为与正则表达式匹配的值运行自定义替换实用程序。这意味着对于正则表达式的每个匹配项,调用包含匹配项的字符的自定义实用程序。自定义实用程序的输出将替换原始字符。

可以使用factor来说明,但这种情况不应该被认为是详尽的,例如可能希望包含带有$%和算术运算符的表达式:

$ factor 230
230: 2 5 23

使用此实用程序并为输入中的所有整数调用它,这是一个示例输入和输出:

$ [code] <<< 'Given these numbers: 27, 13, 230, and 19, it is evident which are primes.'
Given these numbers: 27: 3 3 3, 13: 13, 230: 2 5 23, and 19: 19, it is evident which are primes.

我认为这可能会起作用,但看起来它试图直接解释输入。使用sed (GNU sed) 4.2.2.

$ sed -E 's/([0-9]+)/factor 1/ge' <<< 'Given these numbers: 27, 13, 230, and 19, it is evident which are primes.'
sh: 1: Given: not found

显然我不明白e标志的作用。我尝试在awk这样做:

$ awk '{r = gensub(/([0-9]+)/, system("factor \1"), "g"); print r}' <<< 'Given the numbers with factors: 27, 13, 230, and 19, it is evident which are primes.'
1:
Given the numbers with factors: 0, 0, 0, and 0, it is evident which are primes.

我不确定1:来自哪里,但很明显它只打印了system的返回代码。似乎没有办法从awk中的命令捕获标准输出。

我在核心实用程序中要求的东西是可能的吗?

将数字替换为每个数字上的factor输出

echo "Given these numbers: 27, 13, 230, and 19, it is evident which are primes." | 
perl -wnE'say s{([0-9]+)}{$f=qx(factor $1); chomp $f; $f}egr'

这使用/e修饰符将替换端作为代码进行评估。qx运行命令并返回其输出,换行符由chomp删除。有了/g,它会不断通过字符串来替换所有数字。有关说明,请参见下文。

使用/r修饰符,替换运算符s///返回修改后的字符串。

输出:

给定这些数字:27:3 3 3,13:13,230:2 5 23和19:19,很明显哪些是素数。

请继续阅读其他方法的解释。


原始帖子

例如,从输入行中提取数字并在其上调用每个程序

echo "Given these numbers: 27, 13, 230, and 19, it is evident which are primes."
| perl -wnE'say "Result: ", join(",", map { qx(factor $_) } /([0-9]+)/g)'

列表上下文中的正则表达式匹配运算符返回匹配项,而/g修饰符使其查找所有匹配项。该列表将传递给 map,映射将其主体中的代码应用于每个元素并返回结果列表。qx是反引号的运算符形式,它返回命令的输出,似乎是您想要的。

我将输出的格式留给您,这里的列表由,连接,并以字符串作为前缀。

如果输入行在文件中

perl -wnE'say "Result: ", join(",", map { qx(factor $_) } /([0-9]+)/g)' file

对于更复杂的处理,您可以将匹配列表分配给数组 然后随心所欲地做

perl -wnE'@n = /([0-9]+)/g; for (@n) { say "process $_" }' file

这将一次处理一行输入。

更简单地说,在每个号码匹配时调用程序factor

perl -wnE'while (/([0-9]+)/g) { say qx(factor $1) }' file

while条件中的正则表达式在每次迭代中不断寻找匹配项,从前一个匹配的位置继续,凭借标量上下文中的/g修饰符。在身体内部,您可以对每场捕获的比赛做您需要的事情,这是$1


  † 首次使用/g时的列表上下文 是强加的,因为map将列表作为输入。在第二个上下文中,上下文是标量,因为while条件中的代码被计算为标量。

awk 命令输出中的1来自您打印来自system()的返回代码。system()返回它调用的命令的退出状态,而不是命令的输出 - 它只是转到 stdout。此外,对于您使用的语法,system() 是在 gensub() 之前调用的,因此您使用文字字符串\1调用因子。

正确的语法来做你试图用 GNU awk 做的事情:

awk '{r = gensub(/([0-9]+)/, system("factor \1"), "g"); print r}'

这是可以与任何awk一起使用的吗:

$ awk '{
head = ""
while ( match($0,/[0-9]+/) ) {
cmd = "factor " substr($0,RSTART,RLENGTH)
ret = ( (cmd | getline line) > 0 ? line : cmd " failed" )
close(cmd)
head = head substr($0,1,RSTART-1) ret
$0 = substr($0,RSTART+RLENGTH)
}
$0 = head $0
print
}' <<< 'Given these numbers: 27, 13, 230, and 19, it is evident which are primes.'
Given these numbers: 27: 3 3 3, 13: 13, 230: 2 5 23, and 19: 19, it is evident which are primes.

使用 GNU awk 将第 4 个 arg 转换为 split(),它可以写成:

$ awk '{
n = split($0,others,/[0-9]+/,nums)
$0 = ""
for (i=1; i<n; i++) {
cmd = "factor " nums[i]
ret = ( (cmd | getline line) > 0 ? line : cmd " failed" )
close(cmd)
$0 = $0 others[i] ret
}
$0 = $0 others[n]
print
}' <<< 'Given these numbers: 27, 13, 230, and 19, it is evident which are primes.'
Given these numbers: 27: 3 3 3, 13: 13, 230: 2 5 23, and 19: 19, it is evident which are primes.

以下是替换字符串中文本的解决方案:

perl -MList::Util=pairs -E '($m,$s) = ($_->key,$_->value) and $ARGV[0] =~ s/$m/$s/ for pairs map {$_, qx/factor $_/} $ARGV[0] =~ /(d+)/g; say $ARGV[0]' 
'Given these numbers: 27, 13, 230, and 19, it is evident which are primes'

请注意,factor的输出末尾有一个换行符。

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

sed 's/[0-9]+/$(factor &)/g;s/.*/echo "&"/e' file

用内联函数调用将所有数字括起来,然后用双引号括住整个字符串,并使用echo计算该字符串。

最新更新