2

I would like to have a bash function that prints the specific column of a given string.

My solution works fine but it is verbose and looks ugly to me.

Code:

#!/bin/bash

function get_ip_segment() {
    case "$2" in
        1)
            printf "%s" "$(echo $1 | awk -F '.' '{print $1}')"
            ;;
        2)
            printf "%s" "$(echo $1 | awk -F '.' '{print $2}')"
            ;;
        3)
            printf "%s" "$(echo $1 | awk -F '.' '{print $3}')"
            ;;
        4)
            printf "%s" "$(echo $1 | awk -F '.' '{print $4}')"
            ;;
        *)
            printf "%s" "$1"
            ;;
    esac
}

IP="12.34.56.78"
echo $IP

echo "$(get_ip_segment $IP 1)"
echo "$(get_ip_segment $IP 2)"
echo "$(get_ip_segment $IP 3)"
echo "$(get_ip_segment $IP 4)"

Output:

12.34.56.78
12
34
56
78

Then I tried to optimize this code by removing the switch structure, but it printed an unexpected output.

printf "%s" "$(echo $1 | awk -F '.' '{print column_id}' column_id=$2)"

Output:

1
2
3
4

But somehow, the variable substitution does not work correctly inside awk. That could be great if I could have a one-line long function instead of this verbose one.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
zappee
  • 20,148
  • 14
  • 73
  • 129

4 Answers4

2

You can surely do it like this:

get_ip_segment() {
   awk -F. -v c="$2" '{print 1<=c && c<=NF ? $c : $0}' <<<  "$1"
}

IP="12.34.56.78"

get_ip_segment "$IP" 1
12

get_ip_segment "$IP" 2
34

get_ip_segment "$IP" 3
56

get_ip_segment "$IP" 4
78

get_ip_segment "$IP"
12.34.56.78

get_ip_segment "$IP" 5
12.34.56.78
anubhava
  • 761,203
  • 64
  • 569
  • 643
1

In plain Bash:

get_ip_segment() {
    local output=$1
    if [[ $2 = [1-4] ]]; then
        local -a octets
        IFS=. read -ra octets <<< "$output"
        output=${octets[$2 - 1]}
    fi
    printf '%s\n' "$output"
}

ip=12.34.56.78
get_ip_segment "$ip" 1
get_ip_segment "$ip" 2
get_ip_segment "$ip" 3
get_ip_segment "$ip" 4
get_ip_segment "$ip"

Or, in POSIX shell:

get_ip_segment() {
    case $2 in
        1) output=${1%%.*};;
        2) output=${1#*.}
           output=${output%%.*};;
        3) output=${1%.*}
           output=${output##*.};;
        4) output=${1##*.};;
        *) output=$1
    esac
    printf '%s\n' "$output"
}

ip=12.34.56.78
get_ip_segment "$ip" 1
get_ip_segment "$ip" 2
get_ip_segment "$ip" 3
get_ip_segment "$ip" 4
get_ip_segment "$ip"
M. Nejat Aydin
  • 9,597
  • 1
  • 7
  • 17
1

Using any awk in any POSIX shell:

$ cat tst.sh
#!/usr/bin/env bash

get_ip_segment() { awk -v ip="$1" -v seg="$2" 'BEGIN{ split(ip,a,"."); print (seg in a ? a[seg] : ip) }'; }

ip=12.34.56.78
get_ip_segment "$ip" 1
get_ip_segment "$ip" 2
get_ip_segment "$ip" 3
get_ip_segment "$ip" 4
get_ip_segment "$ip" 0
get_ip_segment "$ip" 5

$ ./tst.sh
12
34
56
78
12.34.56.78
12.34.56.78
Ed Morton
  • 188,023
  • 17
  • 78
  • 185
0
$ ip=12.34.56.78
$ get_ip_segment(){ cut -d. -f $2 <<<$1; }
$ for i in {1..4}; do get_ip_segment $ip $i; done
12
34
56
78
ufopilot
  • 3,269
  • 2
  • 10
  • 12