skip to Main Content

How can I use jq to sort an array of objects by an IPv4 address value as a 32-bit integer rather than alphanumerically?

An illustration of what I mean with some extra formatting for visual clarity:

$ echo '
    {"name": "dns", "ip": "4.4.4.4"}
    {"name": "host1", "ip": "10.9.9.9"}
    {"name": "host2", "ip": "10.10.10.10"}
' |jq -sr '.|=sort_by(.ip) |.[] |[.name, .ip] |@tsv'

host2   10.10.10.10
host1   10.9.9.9
dns 4.4.4.4

This |=sort_by(.ip) sorts in alphanumeric order, but I’d like the array sorted "IPnumerically". If I have it in a TSV format like this, I can use external tools (such as this non-POSIX -V extension to sort) to do this externally afterward, but is there a way to do this within jq?

$ echo '
    {"name": "dns", "ip": "4.4.4.4"}
    {"name": "host1", "ip": "10.9.9.9"}
    {"name": "host2", "ip": "10.10.10.10"}
    ' |jq -sr '.|=sort_by(.ip) |.[] |[.name, .ip] |@tsv' |sort -t$'t' -Vk2
dns 4.4.4.4
host1   10.9.9.9
host2   10.10.10.10

2

Answers


  1. To sort an array of objects by an IPv4 address value as a 32-bit integer using jq, you can convert the IP addresses to integers before sorting. Here’s an example of how you can achieve this:

    echo '
        {"name": "dns", "ip": "4.4.4.4"}
        {"name": "host1", "ip": "10.9.9.9"}
        {"name": "host2", "ip": "10.10.10.10"}
    ' | jq -sr '
      .[]
      | .ip |= split(".") | map(tonumber)
      | .|=sort_by(.ip[0] * 256 * 256 * 256 + .ip[1] * 256 * 256 + .ip[2] * 256 + .ip[3])
      | [.name, .ip | join(".")]
      | @tsv
    '
    

    This will convert each IP address to an array of integers representing the four octets. Then, it calculates the 32-bit integer value by multiplying the respective octets by their corresponding weights and summing them up. Finally, the array is sorted based on the calculated integer values.

    The output will be:

    dns     4.4.4.4
    host1   10.9.9.9
    host2   10.10.10.10
    

    Now, the array is sorted "IPnumerically" based on the integer representation of the IP addresses.

    Update based on a good comment:
    Here’s an updated version of the jq command that sorts the array of objects by IPv4 address value without converting them to a single number:

    echo '
        {"name": "dns", "ip": "4.4.4.4"}
        {"name": "host1", "ip": "10.9.9.9"}
        {"name": "host2", "ip": "10.10.10.10"}
    ' | jq -s 'sort_by(.ip | split(".") | map(tonumber)) | .[] | [.name, .ip] | @tsv'
    
    Login or Signup to reply.
  2. You can simply split the ip and convert it to an array of numbers:

    sort_by(.ip | split(".")[] | tonumber)
    

    Full command (note that the reassigning (.|=) at the beginning is not needed):

    $ echo '
        {"name": "dns", "ip": "4.4.4.4"}
        {"name": "host1", "ip": "10.9.9.9"}
        {"name": "host2", "ip": "10.10.10.10"}
    ' | jq -sr 'sort_by(.ip | split(".")[] | tonumber) | .[] | [.name, .ip] | @tsv'
    
    dns 4.4.4.4
    host1   10.9.9.9
    host2   10.10.10.10
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search