每个人。我有两个文件: 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。谢谢!