Bash:如何将连续输出重定向到文本文件但是使行唯一?

时间:2013-08-22 07:14:46

标签: bash sed awk grep

好的,我有这行将数据输出到文本文件。唯一的问题是我需要线条是唯一的。那么,如果它要添加一条已存在的行,我该怎么办呢?这是我的剧本:

 tcpdump -lvi any "udp port 53" 2>/dev/null|grep -E 'A\?'|awk '{print $(NF-1)}' >> /tmp/domains

我管道输入awk并以某种方式删除重复项吗?我是否有另一个脚本每分钟运行一次,以删除重复项?

以下是加载Amazon.com的输出:

 amazon.com.
 amazon.com.
 www.amazon.com.
 www.amazon.com.
 amazon.com.
 www.amazon.com.
 a0e90b2a1701074fb52d450dc80084cb1.labs.cloudfront.net.
 a0e90b2a1701074fb52d450dc80084cb1.labs.cloudfront.net.
 ad.doubleclick.net.
 ad.doubleclick.net.
 ecx.images-amazon.com.
 ...more

在查看我的输出时,看起来我需要弄清楚为什么会有一个尾随点。

6 个答案:

答案 0 :(得分:2)

你永远不需要grep和awk,因为如果你使用awk,awk可以做grep可以做的任何事情,只需使用awk:

tcpdump -lvi any "udp port 53" 2>/dev/null|
awk '/A\?/{ key=$(NF-1); if (!seen[key]++) print key }' > /tmp/domains

如果您需要停止此脚本并重新启动它,但只将新域附加到输出文件,则只需先读取输出文件以填充“看到”的数组,例如:

tcpdump -lvi any "udp port 53" 2>/dev/null|
awk -v outfile="/tmp/domains" '
    BEGIN{
        while ( (getline key < outfile) > 0 )
            seen[key]++
        close(outfile)
    }
    /A\?/{ key=$(NF-1); if (!seen[key]++) print key >> outfile }
'

答案 1 :(得分:1)

这将只打印出看不见的输入行,而不是像其他一些重复的删除awk脚本一样打印出来。

awk '{host=$(NF-1)} !(host in list) {print host; list[host]++}'

如果你只是想定期运行整个事情并更新列表,那么可能更容易做类似的事情

tcpdump and extract hostnames | sort -u /tmp/domains - > /tmp/domains.new
mv /tmp/domains.new /tmp/domains

答案 2 :(得分:0)

更改此

tcpdump -lvi any "udp port 53" 2>/dev/null|grep -E 'A\?'|awk '{print $(NF-1)}'

要:

tcpdump -lvi any "udp port 53" 2>/dev/null|grep -E 'A\?'|awk '{a[$(NF-1)]++}END{for(i in a)print i}'

答案 3 :(得分:0)

嗯,你需要一个域列表(唯一)吗?或者你需要整条线?

您可以尝试将整行用作awk数组中的键,但时间戳将不同,并且数据包等等。

gawk 'BEGIN{count=0} {arr[$0]=$(NF-1); if (length(arr) > count) { count++; print $0 )}'domain

虽然对你来说可能更有用的是每个域的行......

gawk '{ domain = $(NF-1); arr[ domain ] = $0 ;}  
    END {  for (entry in arr) print "domain:",entry, arr[entry]} '

某些输出对于查看非常有用。 好的,我现在看到了输出,

域必须以点结尾祝你好运!!

PS。使用这个

cmd | gawk 'BEGIN{ count = 0 } { 
             arr[ $0 ] = $(NF-1); 
             if (length(arr) > count) { 
                 count++; 
                 print $0 
             }
      }'  

因为它不断向输出添加新域。最好不要查找域名并改用ips ...

replace $(NF-1) with |& host -t A domain  or so

请参阅gawk信息页面'info gawk'

中的高级功能::双向管道

为了使它有用,您需要将新域插入到排序列表中。虽然我不建议为此使用ncurses,但是将输出汇总到一个java程序,该程序在单个排序表中显示数据也不会难......

答案 4 :(得分:0)

除非您计划长时间运行或拥有非常繁忙的站点,否则您可以通过将先前的查找保存到awk哈希来确保唯一性。这可以在这里工作:

tcpdump -lvi any "udp port 53" 2> /dev/null | grep -E 'A\?' | awk '!h[$(NF-1)]++ { print $(NF-1) }' > /tmp/domains

否则,您需要将tcpdump/grep输出的块保存到临时文件并将其与/tmp/domains合并。我知道的最好的方法是保持输出单独排序,然后与sort -mu进行唯一的合并排序。这可以在这里工作:

lim=10000
tmpfile=$(mktemp /tmp/unique.domain.XXXXXX)
unique_domains=/tmp/domains

tcpdump -lvi any "udp port 53" 2> /dev/null | grep -E 'A\?' | while read line; do
  awk -v lim=$lim '!h[$(NF-1)]++ { print $(NF-1); ndomain++ }; ndomain > lim { exit }' | sort > $tmpfile
  sort -mu $tmpfile $unique_domains 2> /dev/null > $unique_domains.tmp
  mv $unique_domains.tmp $unique_domains
done

如果您想在{} 1}运行时访问/tmp/domain,则需要添加一些文件锁定功能,例如使用lockfile

lim=10000
lock=/tmp/domains.lock
tmpfile=$(mktemp /tmp/unique.domain.XXXXXX)
unique_domains=/tmp/domains

tcpdump -lvi any "udp port 53" 2> /dev/null | grep -E 'A\?' | while read line; do 
  awk -v lim=$lim '!h[$(NF-1)]++ { print $(NF-1); ndomain++ }; ndomain > lim { exit }' | sort > $tmpfile
  lockfile $lock
  sort -mu $tmpfile $unique_domains 2> /dev/null > $unique_domains.tmp
  mv $unique_domains.tmp $unique_domains
  rm $lock
done

现在要获得/tmp/domains的快照,你会做这样的事情:

lockfile /tmp/domains.lock
cp /tmp/domains unique_domains
sync
rm -f /tmp/domains.lock

答案 5 :(得分:0)

答案:

以下是使用管道进行bash功能的解决方案

checkDuplicates() {
    touch -- "$1" # Where $1 is a file that holds the data. It could be the same file that you write to or any other one.
    while read -r nextCheck; do
        grep -q -m 1 "$nextCheck" "$1" || printf "%s\n" "$nextCheck"
    done
}

myFile='/tmp/domains'
YOURANYCOMMAND | checkDuplicates "$myFile" > "$myFile"

奖金技巧:

当您想要查看两个文件之间的差异时,这可能很有用。例如: fileA:

what
is
this

fileB:

what
I
is
dont
this
even

然后这段代码

cat 'fileB' | checkDuplicates 'fileA'

将要输出

I
Dont
Even