Home > Net >  Bash regex parsing ss output
Bash regex parsing ss output

Time:10-08

I'm trying to use a bash regex from a ss comand output to extract the name of the application that is using a given port in a function. I have some problems with the regex.

Command to check the data is ss -tupln

Example of data to parse (ipv4):

tcp    LISTEN   0        10                0.0.0.0:80          0.0.0.0:*    users:(("nc",pid=3474,fd=4))          
tcp    LISTEN   0        10                   [::]:80             [::]:*    users:(("nc",pid=3474,fd=3))  

In this case I want to extract nc which is the name of the program using the port

Example of data to parse (ipv6):

tcp   LISTEN 0      511                 *:80              *:*    users:(("apache2",pid=6741,fd=4),("apache2",pid=6740,fd=4),("apache2",pid=6738,fd=4),("apache2",pid=6737,fd=4),("apache2",pid=6736,fd=4),("apache2",pid=6724,fd=4))

In this case I want to extract apache2 which is the name of the program using the port.

I need a general regex valid for both cases. I don't care if it is get using grep or if is done using pure bash regex. My non-working approach:

#!/bin/bash

get_name() {
    local regexp="${1}.*[0-9\*]:${2}[[:blank:]] .*[[:blank:]] users:\(\(\"(. )\"\,"

    [[ $(ss -tupln) =~ ${regexp} ]] && process_name="${BASH_REMATCH[1]}" 

    echo "${process_name}"
}
get_name "tcp" "80"

Thanks.

CodePudding user response:

You can use awk like that:

$ ss -tupln | awk -v proto=tcp -v port=80 '$1 == proto && $5 ~ ":" port "$" {split($0, array, "\""); print array[2]}'
apache2

CodePudding user response:

In your code, there can also be a ] before 80 which is not matched by the character class.

The last part for the capture group matches too much \"(. )\" as the . can cross matching double quotes.

tcp.*[0-9*\]]:80[[:blank:]] .*[[:blank:]] users:\(\(\"([^"] )\"\,

See a regex demo

For example

#!/bin/bash

get_name() {
    local regexp="${1}.*[0-9*\]]:${2}[[:blank:]] .*[[:blank:]] users:\(\(\"([^\"] )\","
    [[ $(ss -tupln) =~ ${regexp} ]] && process_name="${BASH_REMATCH[1]}" 

    echo "${process_name}"
}
get_name "tcp" "80"

You might also use gnu grep:

ss -tupln | grep -oP 'tcp\h.*?:80\h.*?\busers:\(\("\K[^"] (?=")'

The pattern matches:

  • tcp\h Match tcp and a space
  • .*?:80\h Match as least as possible chars and then :80 and a space
  • .*?\busers: Match as least as possible chars and then users:
  • \(\(" match (("
  • \K[^"] Forget what is matched to far (it will not be part of the resulting match)
  • (?=") Positive lookahead, assert = directly to the right

See a regex demo

  • Related