使用 bash 脚本编辑文件中的某些变量

Edit certain variables in a file using bash script

下面我有以下 SES.ses 文件:

1 VERSION_CONTROL {
2    FILE_NAME = "C:/Users/kjbaili/Documents/SWCD_TEST/SES.ses";
3    DATE = "";
4    VERSION = "1.1";
5    AUTHOR = "";
6  }
7
8  DISPLAYS {
9    DISPLAY xxx-c-vm-0120:main = {
10        COMPUTER = "xxx-C-VM-0120";
11        DISPLAY = "main";
12        ITEMS {
13            PANEL  {
14                name = "visu.pnl";
15            }
16        }
17    }
18   }
19
20   RT-HOSTS {
21    RT-HOST xxx-c-vm-0120 = {
22        COMPONENT  {
23            name = "RTE_connections_xxxxx.cmp";
24        }
25        COMPONENT  {
26            name = "xxxx.cmp";
27        }
28    }
29    RT-HOST xxx-c-agx-0003 = {
30        COMPONENT  {
31            name = "CtApxxx.cmp";
32        }
33        COMPONENT  {
34            name = "CtApxxx.cmp";
35        }
36        COMPONENT  {
37            name = "CtApxxx.cmp";
38        }
39    }
40   }
41
42   HARDWARE {
43    } 

用户输入三个 Input,分别放在 292129 行 -> /userpathDISPLAY Node0RT-HOSTS Node0RT-HOSTS Node1

我正在尝试根据上述用户输入编辑此文件中的变量。它们是:FILE_NAME (line 2)DISPLAY (line 9)RT-HOSTS ( line 21 and 29)

经过一些研究,我可以构建以下内容:

currentPath=$(pwd)/SES    

awk -v path="$currentPath" '/FILE_NAME/ {cnt++} /FILE_NAME/ && cnt==1 {lnee=gensub(/(^.*\")(.*)(\".*$)/,"\1"path"\3",[=12=]);print lnee; next}1' SES.ses > SES.temp && mv -f SES.tmp SES.ses

此命令假定找到 FILE_NAME 的第一个条目并将变量 currentPath 设置为它。但是我收到以下错误:

    awk: cmd. line:1: warning: regexp escape sequence `\"' is not a known regexp operator
mv: cannot stat 'SES.tmp': No such file or directory

所以我的问题是如何解决这个错误以及如何设置其他三个变量line 9, 21 and 29

提前致谢,非常感谢您的帮助

来自@Ed Morton 的建议解决方案:

    awk -v filename='foo' -v display='bar' -v rthosts='some others' 'BEGIN { numRth = split(rthosts,rths) }

(filename != "") && ( == "FILE_NAME") { newval=filename; filename="" }
(display != "")  && ( == "DISPLAY")   { newval=display;   display="" }
(numRth in rths) && ( == "RT-HOST")   { newval=rths[++c]; delete rths[c] }

newval != "" {
    oldval = (  == "=" ?  :  )
    gsub(/^[^"]*"|"[^"]*$/,"",oldval)
    pos = index([=14=],oldval)
    [=14=] = substr([=14=],1,pos-1) newval substr([=14=],pos+length(oldval))
    newval = ""
}

{ print }' SES.ses

cat -Ev SES.ses

的输出
$
VERSION_CONTROL {$
    FILE_NAME = "/c/Users/kjbaili/Documents/DO_NOT_DELETE/SES";$
    DATE = "";$
    VERSION = "1.1";$
    AUTHOR = "";$
}$
$
DISPLAYS {$
    DISPLAY d = {$
        COMPUTER = "FDT-C-VM-0120";$
        DISPLAY = "main";$
        ITEMS {$
            PANEL  {$
                name = "visu.pnl";$
            }$
        }$
    }$
}$
$
RT-HOSTS {$
    RT-HOST v = {$
        COMPONENT  {$
            name = "RTE_connections_CtCoFallbackPath.cmp";$
        }$
        COMPONENT  {$
            name = "CtGwHwpFbpCmp.cmp";$
        }$
    }$
    RT-HOST v = {$
        COMPONENT  {$
            name = "CtApHwpFbpSit.cmp";$
        }$
        COMPONENT  {$
            name = "CtApHwpFbpMpl.cmp";$
        }$
        COMPONENT  {$
            name = "CtApHwpFbpCVGen.cmp";$
        }$
    }$
}$
$
HARDWARE {$
}$

gensub() 函数的第三个参数必须是替换计数 例如 1g(全局)。 你能试试吗:

#!/bin/bash

# user's inputs
read -p "FILE_NAME: " -r file_name
read -p "DISPLAY: " -r display
read -p "RT-HOST: " -r rt_host

awk -v file_name="$file_name" -v display="$display" -v rt_host="$rt_host" '
{
    sub(/FILE_NAME *= *\"[^"]+/, "FILE_NAME = \"" file_name)
    sub(/DISPLAY *[^:]+:/, "DISPLAY " display ":")
}
/RT-HOST / {
    if (! count++)
        sub(/RT-HOST *[^[:space:]]+ *=/, "RT-HOST " display " =")
    else
        sub(/RT-HOST *[^[:space:]]+ *=/, "RT-HOST " rt_host " =")
}
1
' SES.ses > SES.tmp && mv -f -- SES.tmp SES.ses

如果 ed 是 available/acceptable。

这应该显示替换。

#!/usr/bin/env bash

file_name=foobar
display=barmore
rt_host=quxfux

ed -s SES.ses <<-EOF
  2,/FILE_NAME/s/".\{1,\}"/"$file_name"/
  9s|^\([[:blank:]]*DISPLAY\).\{1,\}\(=[[:blank:]]*{$\)| $display |
  21,29s|^\([[:blank:]]*RT-HOST\).\{1,\}\(=[[:blank:]]*{$\)| $rt_host |
  ,p
  Q
EOF

就像 @tshiono 使用内置函数 read 获取 用户的输入一样

#!/usr/bin/env bash

# user's inputs
read -p "FILE_NAME: " file_name
read -p "DISPLAY: " display
read -p "RT-HOST: " rt_host

ed -s SES.ses <<-EOF
  2,/FILE_NAME/s/".\{1,\}"/"$file_name"/
  9s|^\([[:blank:]]*DISPLAY\).\{1,\}\(=[[:blank:]]*{$\)| $display |
  21,29s|^\([[:blank:]]*RT-HOST\).\{1,\}\(=[[:blank:]]*{$\)| $rt_host |
  ,p
  Q
EOF

那里的 ,p 只是显示新编辑缓冲区的输出是什么,将其删除以使输出静音。

Q 更改为 w 以编辑 file-inplace

您收到警告(不是错误)消息 escape sequence '\"' is not a known regexp operator 因为 " 不是正则表达式元字符,它只是一个普通的旧文字字符,如 x,但在你的正则表达式 (^.*\")(.*)(\".*$) 你写了 \" 所以要么:

  1. 你试图在不需要转义时转义 " 或者,
  2. 您正试图在您的正则表达式中包含文字 \ 但是 你需要将其转义为 \ 才能做到这一点。

所以无论哪种方式,您的正则表达式都有问题,所以 awk 会警告您。

我认为这可能是您想要做的:

$ cat tst.awk
BEGIN { numRth = split(rthosts,rths) }

(filename != "") && ( == "FILE_NAME") { newval=filename; filename="" }
(display != "")  && ( == "DISPLAY")   { newval=display;   display="" }
(numRth in rths) && ( == "RT-HOST")   { newval=rths[++c]; delete rths[c] }

newval != "" {
    oldval = (  == "=" ?  :  )
    gsub(/^[^"]*"|"[^"]*$/,"",oldval)
    pos = index([=10=],oldval)
    [=10=] = substr([=10=],1,pos-1) newval substr([=10=],pos+length(oldval))
    newval = ""
}

{ print }

$ awk -v filename='foo' -v display='bar' -v rthosts='some others' -f tst.awk file
VERSION_CONTROL {
   FILE_NAME = "foo";
   DATE = "";
   VERSION = "1.1";
   AUTHOR = "";
 }

 DISPLAYS {
   DISPLAY bar = {
       COMPUTER = "xxx-C-VM-0120";
       DISPLAY = "main";
       ITEMS {
           PANEL  {
               name = "visu.pnl";
           }
       }
   }
  }

  RT-HOSTS {
   RT-HOST some = {
       COMPONENT  {
           name = "RTE_connections_xxxxx.cmp";
       }
       COMPONENT  {
           name = "xxxx.cmp";
       }
   }
   RT-HOST others = {
       COMPONENT  {
           name = "CtApxxx.cmp";
       }
       COMPONENT  {
           name = "CtApxxx.cmp";
       }
       COMPONENT  {
           name = "CtApxxx.cmp";
       }
   }
  }

  HARDWARE {
   }