【问题标题】:Conditional Sort using Awk or sort使用 awk 或 sort 进行条件排序
【发布时间】:2013-10-06 23:30:07
【问题描述】:

好的,所以我在大约一周前asked a question 关于如何使用 sed 或 awk 提取两个空行之间的文本块,以及省略部分提取的文本。我得到的答案几乎满足了我的需求,但现在我正在做一些额外的事情来娱乐(为了强迫症)。

我想在这一轮中对 awk 的输出进行排序。我找到了this question & answer,但它并不能帮助我解决问题。我也尝试过大量的 awk 文档,试图弄清楚如何做到这一点,但无济于事。

所以这是我脚本中完成所有脏工作的代码块:

# This block of stuff fetches the nameservers as reported by the registrar and DNS zone
# Then it gets piped into awk to work some more formatting magic...
# The following is a step-for-step description since I can't put comments inside the awk block:
# BEGIN:
#     Set the record separator to a blank line
#     Set the input/output field separators to newlines
# FNR == 3:
#     The third block of dig's output is the nameservers reported by the registrar
#     Also blanks the last field & strips it since it's just a useless dig comment
dig +trace +additional $host | \
awk -v host="$host" '
    BEGIN {
        RS = "";
        FS = "\n"
    }
    FNR == 3 {
        print "Nameservers of",host,"reported by the registrar:";
        OFS = "\n";
        $NF = ""; sub( /[[:space:]]+$/, "" );
        print
    }
'

如果我将google.com 作为$host 的值传入,这是输出(其他主机名可能会产生不同行数的输出):

Nameservers of google.com reported by the registrar:
google.com.         172800  IN  NS  ns2.google.com.
google.com.         172800  IN  NS  ns1.google.com.
google.com.         172800  IN  NS  ns3.google.com.
google.com.         172800  IN  NS  ns4.google.com.
ns2.google.com.         172800  IN  A   216.239.34.10
ns1.google.com.         172800  IN  A   216.239.32.10
ns3.google.com.         172800  IN  A   216.239.36.10
ns4.google.com.         172800  IN  A   216.239.38.10

这个想法是,使用现有的 awk 块,或者将 awk 的输出通过管道连接到更多 awk、排序或其他任何东西的组合中,使用条件算法对该文本块进行排序:

if ( column 4 == 'NS' )
    sort by column 5
else // This will ensure that the col 1 sort includes A and AAAA records
    sort by column 1

我对答案的偏好与上一个问题几乎相同:

  1. 最重要的是,它必须是可移植的,因为我在使用 sed 时遇到了 OS X(我的家庭系统)和 Fedora(我在工作中使用的)之间的不同行为(必须在 OS X 上用 gsed 替换它)和 grep 的 -m 标志(在另一个脚本中使用)
  2. 非常感谢您解释解决方案的工作原理,因为这是一个比其他任何东西都重要的学习机会。我已经从上一个问题中提供的 awk 解决方案中学到了很多东西。
  3. 如果解决方案可以在同一个 awk 块中实现,那也太棒了
  4. 如果不是,那么我可以通过管道传递 awk 的输出的简单而雄辩的东西就足够了

【问题讨论】:

  • 传统的管道解决方案是添加,正如您所暗示的,另一个步骤是添加一个额外的 'type=1 (or type=2) 列和每一行的末尾,并且同时将所有记录类型重新格式化为可与sort 一起使用的通用格式(通过最简单的方式,可能在行的前面复制列)。来自sort 的管道的最后一步是根据类型列恢复每一行的原始格式。祝你好运。
  • 在这两种情况下,您都可以按1,5 排序,因为type=='NS' 的字段1 是常量

标签: bash shell sorting awk


【解决方案1】:

这是基于@shellter 想法的解决方案。将您的名称服务器记录的输出传送到此:

awk '$4 == "NS" {print $1, $5, $0} $4 == "A" {print $1, $1, $0}' | sort | cut -f3- -d' '

解释:

  • 对于awk,我们只取NSA 记录,并重新打印带有前缀的同一行:主要搜索列+次要搜索列
  • sort 将对行进行排序,感谢我们设置第一列和第二列的方式,顺序应该是你想要的
  • 使用cut,我们去掉了用于排序的前缀

【讨论】:

  • 我不得不稍微修改我的代码,将第一个打印语句放在 awk 之外的一个单独的 echo 语句中以使其工作,但它几乎可以直接开箱即用。我现在正在工作中对此进行测试,因此我必须研究它以了解它在休息期间的工作原理,并根据需要对其进行调整以使其与 IPv6 兼容。谢谢!
  • 现在我有机会在这方面进行更多工作,我能够解决其中的一个小错误:在名称服务器之后按字母顺序排列的主机​​名会导致 NS 和 A 组乱序排列。切割后,我通过 sort -rk 4 进行了管道传输,并且完美地修复了它。 :)
【解决方案2】:

我知道你问过awk 解决方案,但由于你也用bash 标记了它,我想我会提供这样一个版本。它也应该比awk 更便携;)

# the whole line
declare -a lines
# the key to use for sorting
declare -a keys

# insert into the arrays at the appropriate position
function insert
{
    local key="$1"
    local line="$2"
    local count=${#lines[*]}
    local i
    # go from the end backwards
    for((i=count; i>0; i-=1))
    do
        # if we have the insertion point, break
        [[ "${keys[i-1]}" > "$key" ]] || break
        # shift the current item to make room for the new one
        lines[i]=${lines[i-1]}
        keys[i]=${keys[i-1]}
    done
    # insert the new item
    lines[i]=$line
    keys[i]=$key
}

# This block of stuff fetches the nameservers as reported by the registrar and DNS zone
#     The third block of dig's output is the nameservers reported by the registrar
#     Also blanks the last field & strips it since it's just a useless dig comment
block=0
dig +trace +additional $host |
while read f1 f2 f3 f4 f5
do
    # empty line begins new block
    if [ -z "$f1" ]
    then
        # increment block counter
        block=$((block+1))
        # and read next line
        continue
    fi

    # if we are not in block #3, read next line
    [[ $block == 3 ]] || continue

    # ;; ends the block
    if [[ "$f1" == ";;" ]]
    then
        echo "Nameservers of $host reported by the registrar:"
        # print the lines collected so far
        for((i=0; i<${#lines[*]}; i+=1))
        do
            echo ${lines[i]}
        done
        # don't bother reading the rest
        break
    fi

    # figure out what key to use for sorting
    if [[ "$f4" == "NS" ]]
    then
        key=$f5
    else
        key=$f1
    fi
    # add the line to the arrays
    insert "$key" "$f1 $f2 $f3 $f4 $f5"
done

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多