Bash 查找正则表达式模式,将该模式传递给脚本,并用脚本的输出替换该模式

Bash 查找正则表达式模式,将该模式传递给脚本,并用脚本的输出替换该模式,bash,sed,scripting,Bash,Sed,Scripting,每当模式出现时(在本例中为2位数字),我都希望将该模式传递给脚本,并用脚本的输出替换该模式 我用sed作为例子,说明它应该是什么样子 echo 'siedi87sik65owk55dkd' | sed 's/[0-9][0-9]/.\/script.sh/g' 现在它回来了 siedi./script.shsik./script.showk./script.shdkd 但我希望它能回来 siedi!!!87!!!sik!!!65!!!owk!!!55!!!dkd 这就是./script.s

每当模式出现时(在本例中为2位数字),我都希望将该模式传递给脚本,并用脚本的输出替换该模式

我用sed作为例子,说明它应该是什么样子

echo 'siedi87sik65owk55dkd' | sed 's/[0-9][0-9]/.\/script.sh/g'
现在它回来了

siedi./script.shsik./script.showk./script.shdkd
但我希望它能回来

siedi!!!87!!!sik!!!65!!!owk!!!55!!!dkd
这就是./script.sh中的内容

#!/bin/bash

echo "!!!$1!!!"

必须用输出替换它。在这个例子中,我知道我可以使用一个普通的sed替换,但我不想把它作为一个答案。

sed只用于单个行上的简单替换,仅此而已。其他任何事情,即使可以做到,都需要神秘的语言结构,这种结构在20世纪70年代中期awk发明时就已经过时,今天纯粹是用于心理训练。您的问题不是简单的替换,因此您不应该尝试使用sed来解决它

您将需要以下内容:

awk '{
    head = ""
    tail = $0
    while ( match(tail,/[0-9]{2}/) ) {
        tgt = substr(tail,RSTART,RLENGTH)
        cmd = "./script.sh " tgt
        if ( (cmd | getline line) > 0) {
            tgt = line
        }
        close(cmd)
        head = head substr(tail,1,RSTART-1) tgt
        tail = substr(tail,RSTART+RLENGTH)
    }
    print head tail
}'
e、 g.使用
echo
代替
script.sh
命令:

$ echo 'siedi87sik65owk55dkd' |
awk '{
    head = ""
    tail = $0
    while ( match(tail,/[0-9]{2}/) ) {
        tgt = substr(tail,RSTART,RLENGTH)
        cmd = "echo !!!" tgt "!!!"
        if ( (cmd | getline line) > 0) {
            tgt = line
        }
        close(cmd)
        head = head substr(tail,1,RSTART-1) tgt
        tail = substr(tail,RSTART+RLENGTH)
    }
    print head tail
}'
siedi!!!87!!!sik!!!65!!!owk!!!55!!!dkd

sed是对单个行的简单替换,仅此而已。其他任何事情,即使可以做到,都需要神秘的语言结构,这种结构在20世纪70年代中期awk发明时就已经过时,今天纯粹是用于心理训练。您的问题不是简单的替换,因此您不应该尝试使用sed来解决它

您将需要以下内容:

awk '{
    head = ""
    tail = $0
    while ( match(tail,/[0-9]{2}/) ) {
        tgt = substr(tail,RSTART,RLENGTH)
        cmd = "./script.sh " tgt
        if ( (cmd | getline line) > 0) {
            tgt = line
        }
        close(cmd)
        head = head substr(tail,1,RSTART-1) tgt
        tail = substr(tail,RSTART+RLENGTH)
    }
    print head tail
}'
e、 g.使用
echo
代替
script.sh
命令:

$ echo 'siedi87sik65owk55dkd' |
awk '{
    head = ""
    tail = $0
    while ( match(tail,/[0-9]{2}/) ) {
        tgt = substr(tail,RSTART,RLENGTH)
        cmd = "echo !!!" tgt "!!!"
        if ( (cmd | getline line) > 0) {
            tgt = line
        }
        close(cmd)
        head = head substr(tail,1,RSTART-1) tgt
        tail = substr(tail,RSTART+RLENGTH)
    }
    print head tail
}'
siedi!!!87!!!sik!!!65!!!owk!!!55!!!dkd
Ed's显然是一个不错的选择

为了好玩,我尝试提出一个sed解决方案,这里是一个(复杂的gnused)解决方案,它将运行的模式和脚本作为参数;输入可以从标准输入读取(即,可以通过管道读取),也可以从作为第三个参数提供的文件读取

例如,我们将用内容填充
infle

siedi87sik65owk55dkd
siedi11sik22owk33dkd
#!/bin/bash

echo "!!!${1}!!!"
(用两行代码演示如何在多行代码中使用),然后使用内容

siedi87sik65owk55dkd
siedi11sik22owk33dkd
#!/bin/bash

echo "!!!${1}!!!"
最后是解决方案脚本本身,
so
。用法是

./so pattern script [input]
或者,作为过滤器

cat infile | ./so '[[:digit:]]{2}' script
有输出

siedi!!!87!!!sik!!!65!!!owk!!!55!!!dkd
siedi!!!11!!!sik!!!22!!!owk!!!33!!!dkd
这就是
的样子

#!/bin/bash

pat=$1                      # The pattern to match
script=$2                   # The command to run for each pattern
infile=${3:-/dev/stdin}     # Read from standard input if not supplied

# Use sed and have $pattern and $script expand to the supplied parameters
sed -r "
    :build_loop                        # Label to loop back to
    h                                  # Copy pattern space to hold space
    s/.*($pat).*/.\/\"$script\" \1/    # (1) Extract last match and prepare command
    # Replace pattern space with output of command
    e
    G                                  # (2) Append hold space to pattern space
    s/(.*)$pat(.*)/\1~~~\2/            # (3) Replace last match of pattern with ~~~
    /\n[^\n]*$pat[^\n]*$/b build_loop  # Loop if string contains match
    :fill_loop                         # Label for second loop
    s/(.*\n)(.*)\n([^\n]*)~~~([^\n]*)$/\1\3\2\4/ # (4) Replace last ~~~
    t fill_loop                        # Loop if there was a replacement
    s/(.*)\n(.*)~~~(.*)$/\2\1\3/       # (5) Final ~~~ replacement
" < "$infile"
然后,
e
命令(GNU扩展)用该命令的输出替换模式空间。在此之后,
G
将保持空间附加到图案空间(2)。图案空间现在如下所示:

./script 55
!!!55!!!
siedi87sik65owk55dkd
!!!87!!!
!!!65!!!
!!!55!!!
siedi~~~sik~~~owk~~~dkd
(3)处的替换将最后一个匹配替换为一个字符串,希望该字符串不等于模式,我们得到

!!!55!!!
siedi87sik65owk~~~dkd
如果模式空间的最后一行仍然与模式匹配,则循环将重复。经过三次循环后,图案空间如下所示:

./script 55
!!!55!!!
siedi87sik65owk55dkd
!!!87!!!
!!!65!!!
!!!55!!!
siedi~~~sik~~~owk~~~dkd
现在,第二个循环将模式空间的最后一行替换为替换(4)。该命令使用了大量的“非换行符”(
[^\n]
)来确保我们没有为
~~
进行错误的替换

由于命令(4)的编写方式,循环以最后一次替换结束,因此在命令(5)之前,我们有以下模式空间:

!!!87!!!
siedi~~~sik!!!65!!!owk!!!55!!!dkd
命令(5)是命令(4)的一个更简单版本,在它之后,输出是所需的

这似乎相当健壮,可以处理要运行的脚本名称中的空格,只要在调用时正确引用:

./so '[[:digit:]]{2}' 'my script' infile
如果

  • 输入文件包含
    ~~
    (可通过替换开头的所有引用,将它们放回末尾来解决)
  • 脚本的输出包含
    ~~
  • 模式包含
    ~~
i、 例如,解决方案在很大程度上取决于
~
的唯一性


因为没有人问:
so
作为一行

#/bin/bash
sed-re:b;h;s/*($1)。*/.\/\“$2\”\1/;e“-e”G;s/(.*)$1(.*)/\1~~~\2/;/\n[^\n]*$1[^\n]*$/bb;:f;s/(.\n)(.*)\n([^\n]*)~([^\n]*)$/\1\3\2\4/;“$tf
仍然有效

Ed's显然是一个不错的选择

为了好玩,我尝试提出一个sed解决方案,这里是一个(复杂的gnused)解决方案,它将运行的模式和脚本作为参数;输入可以从标准输入读取(即,可以通过管道读取),也可以从作为第三个参数提供的文件读取

例如,我们将用内容填充
infle

siedi87sik65owk55dkd
siedi11sik22owk33dkd
#!/bin/bash

echo "!!!${1}!!!"
(用两行代码演示如何在多行代码中使用),然后使用内容

siedi87sik65owk55dkd
siedi11sik22owk33dkd
#!/bin/bash

echo "!!!${1}!!!"
最后是解决方案脚本本身,
so
。用法是

./so pattern script [input]
或者,作为过滤器

cat infile | ./so '[[:digit:]]{2}' script
有输出

siedi!!!87!!!sik!!!65!!!owk!!!55!!!dkd
siedi!!!11!!!sik!!!22!!!owk!!!33!!!dkd
这就是
的样子

#!/bin/bash

pat=$1                      # The pattern to match
script=$2                   # The command to run for each pattern
infile=${3:-/dev/stdin}     # Read from standard input if not supplied

# Use sed and have $pattern and $script expand to the supplied parameters
sed -r "
    :build_loop                        # Label to loop back to
    h                                  # Copy pattern space to hold space
    s/.*($pat).*/.\/\"$script\" \1/    # (1) Extract last match and prepare command
    # Replace pattern space with output of command
    e
    G                                  # (2) Append hold space to pattern space
    s/(.*)$pat(.*)/\1~~~\2/            # (3) Replace last match of pattern with ~~~
    /\n[^\n]*$pat[^\n]*$/b build_loop  # Loop if string contains match
    :fill_loop                         # Label for second loop
    s/(.*\n)(.*)\n([^\n]*)~~~([^\n]*)$/\1\3\2\4/ # (4) Replace last ~~~
    t fill_loop                        # Loop if there was a replacement
    s/(.*)\n(.*)~~~(.*)$/\2\1\3/       # (5) Final ~~~ replacement
" < "$infile"
然后,
e
命令(GNU扩展)用该命令的输出替换模式空间。在此之后,
G
将保持空间附加到图案空间(2)。图案空间现在如下所示:

./script 55
!!!55!!!
siedi87sik65owk55dkd
!!!87!!!
!!!65!!!
!!!55!!!
siedi~~~sik~~~owk~~~dkd
(3)处的替换将最后一个匹配替换为一个字符串,希望该字符串不等于模式,我们得到

!!!55!!!
siedi87sik65owk~~~dkd
如果模式空间的最后一行仍然与模式匹配,则循环将重复。经过三次循环后,图案空间如下所示:

./script 55
!!!55!!!
siedi87sik65owk55dkd
!!!87!!!
!!!65!!!
!!!55!!!
siedi~~~sik~~~owk~~~dkd
现在,第二个循环将模式空间的最后一行替换为替换(4)。该命令使用了大量的“非换行符”(
[^\n]
)来确保我们没有为
~~
进行错误的替换

由于命令(4)的编写方式,循环以最后一次替换结束,因此在命令(5)之前,我们有以下模式空间:

!!!87!!!
siedi~~~sik!!!65!!!owk!!!55!!!dkd
命令(5)是命令(4)的一个更简单版本,在它之后,输出是所需的

这似乎相当健壮,可以处理要运行的脚本名称中的空格,只要在调用时正确引用:

./so '[[:digit:]]{2}' 'my script' infile
如果

  • 输入文件包含
    ~~
    (可通过替换所有