![Bash 脚本将一个文件作为输入并对另一个文件运行 awk 命令](https://linux22.com/image/126035/Bash%20%E8%84%9A%E6%9C%AC%E5%B0%86%E4%B8%80%E4%B8%AA%E6%96%87%E4%BB%B6%E4%BD%9C%E4%B8%BA%E8%BE%93%E5%85%A5%E5%B9%B6%E5%AF%B9%E5%8F%A6%E4%B8%80%E4%B8%AA%E6%96%87%E4%BB%B6%E8%BF%90%E8%A1%8C%20awk%20%E5%91%BD%E4%BB%A4.png)
我正在尝试编写一个脚本,该脚本接受一个文件并将每一行作为 awk 命令的字符串运行,该命令在另一个文件上执行。这是我目前所拥有的。
#!/bin/bash
FILE=$1
FILE_TO_SEARCH=$2
exec 4> "FILE_TO_SEARCH"
while read -ru 3 LINE; do
awk -v RS='' -v ORS='\n\n' "$LINE" <&4
done 3< "$FILE"
当我尝试运行脚本时,我得到:
./bashscript2.sh: line 8: read: read error: 3: Bad file descriptor
作为示例,它将搜索的文件 (FILE) 的内容如下:
hostAbC
host123
host345
hostMos
hostDef
然后,它将在一个文件 (FILE_TO_SEARCH) 上运行 awk 命令,其内容类似于下面的内容,但还有更多内容。
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
id: urn:storageos:Initiator:
clustername = BLAHBLAHBLAH
creationTime = java.util.GregorianCalendar[
time=1490279415811
2017-03-23 14:30:15 811ms UTC
,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="UTC",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2017,MONTH=2,WEEK_OF_YEAR=12,WEEK_OF_MONTH=4,DAY_OF_MONTH=23,DAY_OF_YEAR=82,DAY_OF_WEEK=5,DAY_OF_WEEK_IN_MONTH=4,AM_PM=1,HOUR=2,HOUR_OF_DAY=14,MINUTE=30,SECOND=15,MILLISECOND=811,ZONE_OFFSET=0,DST_OFFSET=0]
host = URI:
hostname = hostAbC
inactive = false
ininode = 01:01:01:01:01:01:01:01
iniport = 01:01:01:01:01:01:01:01
internalFlags = 0
isManualCreation = true
label = 01:01:01:01:01:01:01:01
status = OpStatusMap {}
protocol = FC
registrationStatus = REGISTERED
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
id: urn:storageos:Initiator:
clustername = YADAYADAYADA
creationTime = java.util.GregorianCalendar[
time=1485972630239
2017-02-01 18:10:30 239ms UTC
,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="UTC",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2017,MONTH=1,WEEK_OF_YEAR=5,WEEK_OF_MONTH=1,DAY_OF_MONTH=1,DAY_OF_YEAR=32,DAY_OF_WEEK=4,DAY_OF_WEEK_IN_MONTH=1,AM_PM=1,HOUR=6,HOUR_OF_DAY=18,MINUTE=10,SECOND=30,MILLISECOND=239,ZONE_OFFSET=0,DST_OFFSET=0]
host = URI:
hostname = hostMos
inactive = false
ininode = 01:01:01:01:01:01:01:01
iniport = 01:01:01:01:01:01:01:01
internalFlags = 0
isManualCreation = false
label = 01:01:01:01:01:01:01:01
status = OpStatusMap {}
protocol = FC
registrationStatus = REGISTERED
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
然后awk 命令awk -v RS='' -v ORS='\n\n'
将找到该hostAbC
条目并返回从一个空间到另一个空间的完整条目。
我不知道如何让它发挥作用。
答案1
exec 4> "FILE_TO_SEARCH"
问题是您打开文件仅用于写入(从而删除其内容),但尝试使用文件描述符进行读取。
exec 4< "$FILE_TO_SEARCH"
答案2
正如我的评论中所指出的,除非有一些您没有告诉我们的事情,否则似乎没有必要在此处使用除 stdin 和 stdout 之外的文件描述符,也没有任何理由不将文件名传递awk
给命令行。
编写一个 shellwhile read
循环来对同一个输入文件运行awk
多次是一种非常糟糕的方式来完成您想要做的事情 - 可能是处理文本文件的最糟糕的方式。它比在 awk(或 sed 或 perl 等)中执行相同的任务慢数百或数千倍。
尝试这样的事情:
#!/bin/bash
FILE1="$1"
FILE_TO_SEARCH="$2"
awk 'NR==FNR { gsub(/([\\.^$(){}\[\]|*+?])/,"\\\\&",$0);
if (search == "") {
search = $0;
} else {
search = search "|" $0;
};
next;
};
match($0,search)' "$FILE1" RS='' ORS='\n\n' "$FILE_TO_SEARCH"
(为了可读性而添加换行、缩进。该作品的 awk 部分也全部压缩在一行上)
这将打印出与$FILE_TO_SEARCH
中任何搜索模式匹配的所有记录$FILE1
。
$FILE1
它使用默认的RS
&读取第一个文件 ( ) ORS
,并从中构造正则表达式搜索模式。该gsub()
函数调用用于在将每行附加到搜索模式之前对所有正则表达式元字符进行反斜杠转义,即所有行都被视为固定字符串。如果您希望每一行都是正则表达式,请参阅下面的第二个版本。
对于上面的示例$FILE1
,搜索模式将是:
hostAbC|host123|host345|hostMos|hostDef
然后,使用RS=''
和ORS='\n\n'
读入第二个文件 ( $FILE_TO_SEARCH
) 并打印与搜索模式匹配的任何记录。
$FILE1
如果您希望将每一行解释为正则表达式而不是固定字符串,则可以使用以下版本:
#!/bin/bash
FILE1="$1"
FILE_TO_SEARCH="$2"
awk 'NR==FNR { if (search == "") {
search = "(" $0 ")" ;
} else {
search = search "|(" $0 ")";
};
next;
};
match($0,search)' "$FILE1" RS='' ORS='\n\n' "$FILE_TO_SEARCH"
此版本的示例搜索模式为:
(hostAbC)|(host123)|(host345)|(hostMos)|(hostDef)
请注意,使用此版本很容易构建一个损坏的搜索模式,该模式要么不匹配任何内容,要么匹配太多。您将需要使用反斜杠来转义 $FILE1 中您想要解释为文字字符串的任何正则表达式元字符。例如,如果您想匹配文字,|
那么它必须位于文件中 as \|
,否则它将被解释为正则表达式OR
交替运算符。