提问人:Wolverine adamantium 提问时间:8/14/2023 最后编辑:Renaud PacaletWolverine adamantium 更新时间:8/15/2023 访问量:95
查询第 5 列值,基于第 4 列输入,其中第 4 列由第一个文件提供 .txt
Query 5th column value, based on 4th column input, where 4th column provided by first file.txt
问:
我有 2 个文件,
first file.txt
tskvdsc95
tosaocs
second file.txt
crbvdsc85;172.31.216.65&172.31.216.66;2016;tskvdsc95;172.31.240.65&172.31.240.66;3016
crbvdsc85;172.31.216.65&172.31.216.66;2017;tskvdsc95;172.31.240.65&172.31.240.66;3017
tskvdsc195.epc.mnc009.mcc510.3gppnetwork.org;172.20.197.3;3412;tosaocs;172.20.237.70;3412
tskvdsc195.epc.mnc009.mcc510.3gppnetwork.org;172.20.197.3;3413;tosaocs;172.20.237.69;3413
我需要查询第二个文件中的第 5 列,使用第一个文件中的数据作为第 4 列引用的输入。
贝娄是我的剧本
#!/bin/bash
input="/path/to/folder/first file.txt.txt"
while IFS= read -r line
do
awk 'BEGIN{FS=";"} $4=="$line" {print$5}' /path/to/folder/second file.txt | sort | uniq -c
#echo "$line"
done < "$input"
我的脚本正在运行,结果不是我所期望的。
我的预期结果应该是:
172.31.240.65&172.31.240.66
172.20.237.70
172.20.237.69
请帮助上面的脚本中哪个部分是错误的。
提前致谢,
WF系列
答:
0赞
cforler
8/14/2023
#1
以下脚本生成预期的输出。
#!/bin/sh
file1=$1
file2=$2
while IFS= read -r target <&3; do
{
while IFS= read -r line <&4; do
{
column=$(echo "$line" | cut -d ";" -f 4)
if [ "$column" = "$target" ]; then
echo "$line" | cut -d ';' -f 5
fi
} 3<&-
done 4< "$file2" | sort -rnu
} 4<&-
done 3< "$file1"
我已经解决了 Ed Morton 指出的几个问题。
- 摆脱了 https://www.shellcheck.net/ 的所有警告。
- 将大写名称替换为小写名称,如文章 correct-bash-and-shell-script-variable-capitalization 所建议的那样。
- 改进了可靠性,正如文章 why-is-using-a-shell-loop-to-process-text-considered-bad-practice 所指出的那样
- 修复了部分正则表达式匹配问题。
评论
0赞
Ed Morton
8/18/2023
AFAIK 唯一剩下的问题是它会慢几个数量级,并且显然需要比 awk 脚本更多的代码和更复杂的代码,但您对此无能为力。
0赞
Wolverine adamantium
8/18/2023
谢谢你的回答。我的问题解决了
3赞
Renaud Pacalet
8/14/2023
#2
您显然希望避免输出中的重复。 可能是这项工作的不错选择,这要归功于它的关联数组,以及在字段中拆分输入记录的能力。awk
如果您的输入格式很简单(在引号字段中没有,每行一条记录等),您可以尝试:;
awk -F';' 'NR==FNR {a[$0];next} $4 in a {b[$5]}
END {for(k in b) print k}' file1 file2
声明为输入字段分隔符 ()。在解析第一个文件时(仅第一个文件为 true),将每一行存储为数组 () 的键并移动到下一行 ()。在解析第二个文件时,如果第四个字段是数组 () 的键,则将第五个字段存储在数组 中。在数组 () 的所有键上的循环中并打印它们 ()。;
-F';'
NR==FNR
a
a[$0]
next
a
$4 in a
b
END
b
for(k in b)
print k
注意:这样可以避免输出中的重复,但不会保留输入顺序。如果您需要保留输入顺序,请编辑您的问题并添加此内容。
评论
0赞
Wolverine adamantium
8/16/2023
感谢@Renault帕卡莱特,我想要的那个工作级长。对不起,回复晚了,我正在谷歌上搜索脚本的工作原理。我可以再次要求进一步增强吗,上面的脚本可以很好地避免第 5 列中的重复,但丢失了第 5 列属于第 4 列而没有重复的信息。
0赞
Renaud Pacalet
8/16/2023
不确定我是否理解。是否也要打印第 4 列?这与预期的输出不同,但很容易做到:替换为 和 。{b[$5]}
{b[$5]=$4}
print k
print b[k] ";" k
0赞
Wolverine adamantium
8/16/2023
是的,按照我的预期工作。同时显示第 4 列和第 5 列。所以基本上,如果没有错误的话,将 b[$5] 打印为索引,将 $4 打印为值?非常感谢您的帮助
0赞
Renaud Pacalet
8/16/2023
就是这样。在答案中,我们只使用键,而不关心值。在这里,我们确实同时使用了两者。
4赞
Ed Morton
8/14/2023
#3
关于错误 - 请阅读 how-do-i-use-shell-variables-in-an-awk-script。但是不要为此使用每行调用 awk 的 shell 循环,只需调用 awk 一次。$4=="$line"
使用任何 awk:
$ cat tst.sh
#!/usr/bin/env bash
awk -F';' '
NR==FNR {
first[$1]
next
}
($4 in first) && !seen[$5]++ {
print $5
}
' first_file.txt second_file.txt
$ ./tst.sh
172.31.240.65&172.31.240.66
172.20.237.70
172.20.237.69
评论
.txt