通过 awk 使用列表文件编辑主文件

通过 awk 使用列表文件编辑主文件

每个人。我有两个文件: ports.lst 和 master.tbl

ports.lst 看起来像这样:

hawaii-P1
hawaii-P2
hawaii-P3
losangeles-P1
losangeles-P3

master.tbl 看起来像这样:

#Site 1 Honolulu
servername HAWAII-A hawaii-P1 InitFileA OutFileA otherfields
servername HAWAII-A hawaii-P2 InitFileA OutFileA otherfields
#servername HAWAII-A hawaii-P3 InitFileA OutFileA otherfields
servername HAWAII-A hawaii-P4 InitFileA OutFileA otherfields

#Site 16 Dallas
servername DALLAS-A dallas-P1 InitFileA OutFileA otherfields
servername DALLAS-A dallas-P2 InitFileA OutFileA otherfields
servername DALLAS-A dallas-P3 InitFileA OutFileA otherfields

#Site 8 L.A.
#servername LOSANGELES-A losangeles-P1 InitFileA OutFileA otherfields
servername LOSANGELES-A losangeles-P2 InitFileA OutFileA otherfields
#servername LOSANGELES-A losangeles-P3 InitFileA OutFileA otherfields

我需要在 master.tbl 文件中搜索 ports.lst 中列出的每个端口,并替换“InitFileA”和“OutFileA”,使文件看起来像这样:

#Site 1 Honolulu
servername HAWAII-A hawaii-P1 InitFileB-hawaii-username-ALPHA-password OutFileB-hawaii-username-ALPHA otherfields
servername HAWAII-A hawaii-P2 InitFileB-hawaii-username-ALPHA-password OutFileB-hawaii-username-ALPHA otherfields
#servername HAWAII-A hawaii-P3 InitFileB-hawaii-username-ALPHA-password OutFileB-hawaii-username-ALPHA otherfields
servername HAWAII-A hawaii-P4 InitFileA OutFileA otherfields

#Site 16 Dallas
servername DALLAS-A dallas-P1 InitFileA OutFileA otherfields
servername DALLAS-A dallas-P2 InitFileA OutFileA otherfields
servername DALLAS-A dallas-P3 InitFileA OutFileA otherfields

#Site 8 L.A.
#servername LOSANGELES-A losangeles-P1 InitFileB-losangeles-username-ALPHA-password OutFileB-losangeles-username-ALPHA otherfields
servername LOSANGELES-A losangeles-P2 InitFileA OutFileA otherfields
#servername LOSANGELES-A losangeles-P3 InitFileB-losangeles-username-ALPHA-password OutFileB-losangeles-username-ALPHA otherfields

这就是我现在所处的位置,但显然它失败了。

awk 'NR==FNR{z[$0];next}{if ($3 in z && $4 == "InitFileA"){ c=(echo $3| awk -F '-' {print $1});$4="InitFileB-"c"-username-ALPHA-password";$5="OutFileB-"c"-username-ALPHA"}}1' ports.lst master.tbl > output.tbl

我也尝试过:

awk 'NR==FNR{z[$0];next}{if ($3 in z && $4 == "InitFileA"){ c=$3; sub(/-.*/, "", $c);$4="InitFileB-"c"-username-ALPHA-password";$5="OutFileB-"c"-username-ALPHA"}}1' ports.lst master.tbl > output.tbl

我一直在为这件事揪心。这里有人可以对我做错了什么提供任何见解吗?

答案1

您有将任务分为两轮的正确基本想法,但是然后您在 awk 规则中调用 awk.. 这就是我停止阅读它的地方;解决如此简单的问题的方法太复杂了。

考虑这个 awk 片段:

awk 'BEGIN {
         RS = "[\t\v\f ]*(\r\n|\n\r|\r|\n)";
         FS = "[\t\v\f ]+"
     }

     FNR==1 {
         file++
     }

     /^#/ {
         next
     }

     file==1 {
         port[$1] = $1
     }

     file>=2 && ($3 in port) {
         base = $3;
         sub(/-[^-]*$/, "", base);
         $4 = "InitFileB-" base "-username-ALPHA-password";
         $5 = "OutFileB-" base "-username-ALPHA";
     }

     file>=2 {
         printf "%s\n", $0
     } ' ports.lst master.tbl

注意:我添加了必要的分号,因此您可以将以上所有内容写在一行中。

如果您使用示例输入文件运行上面的代码,您将得到

losangeles-P1
losangeles-P3
servername HAWAII-A hawaii-P1 InitFileB-hawaii-username-ALPHA-password OutFileB-hawaii-username-ALPHA otherfields
servername HAWAII-A hawaii-P2 InitFileB-hawaii-username-ALPHA-password OutFileB-hawaii-username-ALPHA otherfields
servername HAWAII-A hawaii-P4 InitFileA OutFileA otherfields

servername DALLAS-A dallas-P1 InitFileA OutFileA otherfields
servername DALLAS-A dallas-P2 InitFileA OutFileA otherfields
servername DALLAS-A dallas-P3 InitFileA OutFileA otherfields

servername LOSANGELES-A losangeles-P2 InitFileA OutFileA otherfields

BEGIN规则仅设置通用换行符支持,以防文件从具有不同换行符编码的其他系统(例如 Windows)传输。

FNR==1规则用于更新file变量,以便它反映正在处理的文件(1 表示第一个,2 第二个)。

/^#/ { next }规则会跳过以哈希标记开头的所有行。它们是注释,因此不需要保留。/^[\t\v\f ]*$/ { next }如果您想压缩输出文件,我们还可以添加一条规则来跳过所有空行。

file == 1 { port[$1] = $1 }规则将第一个文件中的所有第一个字段添加到关联数组中port。分配的值 ( = $1) 并不重要,所以我们实际上可以= 0在这里使用。

file >= 2 && ($3 in port)规则适用于第二个和任何后续文件,并且如果第三个字段与关联数组中的键之一匹配,则执行该规则port。 (值并不重要;仅检查键。)换句话说,仅当第三个字段是端口列表中指定的键之一时才应用此规则。

第三个字段被复制到一个变量base- 这与中的键之一匹配port[]- 并且最后一个字段之后的所有内容-都使用 删除sub()。然后,我们修改第四和第五字段。请注意,在 awk 中,没有字符串连接运算符;我们只需将字符串放在一起即可。换句话说,("foo" a "bar")是一个字符串,由“foo”组成,紧接着是转换为字符串的变量值a,紧接着是“bar”。

最终规则打印(可能已修改的)记录,但确保\n使用换行符。仅使用第二个及后续文件中的记录。

现在,如果ports.lst包含各自的用户名和密码,我会稍微修改上面的内容(可能更改了三行?),但我希望您可以看到整体方法。

答案2

我似乎已经找到答案了。我的问题似乎出在“c”变量附近的美元符号的位置。也就是说,这有效:

awk 'NR==FNR{z[$0];next} { if ($3 in z && $4 == "InitFileA"){ c=$3; sub(/-.*/, "", c);$4="InitFileB-"c"-username-ALPHA-password";$5="OutFileB-"c"-username-ALPHA"}}1' ports.lst master.tbl > output.tbl

现在,至于为什么它有效,恐怕我无法解释。我确实抓住了救命稻草,变得绝望。我采取了剥离命令层的方式,直到错误消失,然后慢慢添加命令并进行修补,直到每个命令都起作用。

答案3

我没有做足够的 awk 脚本来仅输入语句,但我会寻找一种使用“主”文件结构并具有多个块的方法。

概念解决方案

BEGIN
{
#  get it ready ...
}

/^$/
{
# maybe just skip lines
# otherwise potential post processing for #Site XX Name
}

/^#Site/
{
# initialize processing for a new site
}

{
# default block for the site processing 'input'
}

FINISH
{
# 'master' is parsed - now fill in the blanks using 'port'
# i.e, start of second pass to complete the work
}

我知道其中没有 awk 命令 - 但我也很好奇 awk 专家是否将其视为 awk 解决方案的通用方法。我经常拒绝使用 awk,因为我迷失在“单行 awk 语句”中,即一个命令将处理所有行,无论是否存在重复的输入块。

而且,即使这被证明是荒谬的方法 - 我希望这些评论能够启发我(和其他人),以便我更好地使用 awk。谢谢!

相关内容