skip to Main Content

In my ubuntu instance I have a file which looks like this, actually is not a file but an output of the following command:

cat wghub.conf | egrep '.conf|AllowedIPs' | grep -v configuration | awk '{print $3}'|cut -d "/" -f1|tr "n" " "|xargs -n 2

samsung 10.88.188.11
ultra 10.88.188.12
mi10 10.88.188.13
drunksorrow 10.88.188.15
win11 10.88.188.16
jderu 10.88.188.17

I also have a second file generated by crontab once per minute:

Client: 10.88.188.13, Latest Handshake: 1 minute, 42 seconds
Client: 10.88.188.16, Latest Handshake: 51 minutes, 32 seconds

I need to replace the IP with the correspondent name from the 1st file into the 2nd file so the desired result will be:

Client: mi10, Latest Handshake: 1 minute, 42 seconds
Client: win11, Latest Handshake: 51 minutes, 32 seconds

How can I achieve this with bash?
I tried in so many ways with no results.
Thank you!

I have no results because I can’t join the IPs from the both files in order to format the second file as I want

3

Answers


  1. Append this to your pipe to generate the necessary commands for sed in a file:

    | sed -E 's|(.*)( .*)|s/2/ 1/|' >search_and_replace.sed
    

    Then execute:

    sed -f search_and_replace.sed file_from_cron.txt
    

    Output:

    Client: mi10, Latest Handshake: 1 minute, 42 seconds
    Client: win11, Latest Handshake: 51 minutes, 32 seconds
    
    Login or Signup to reply.
  2. In bash you can use associative array (map):

    #!/bin/bash
    
    declare -A HOSTMAP
    
    # read "address to name" map from file
    while read NAME ADDR; do
        HOSTMAP[$ADDR]="$NAME"
    done < hostmap.txt
    
    # read each line of client into array
    while read -a LINE; do
        ADDR="${LINE[1]%,}"          # remove trailing comma
        [ -z "$ADDR" ] && continue   # skip if empty
        LINE[1]="${HOSTMAP[$ADDR]}," # substitute address with name, add trailing comma
        echo ${LINE[@]}
    done < clients.txt
    

    Output:

    Client: mi10, Latest Handshake: 1 minute, 42 seconds
    Client: win11, Latest Handshake: 51 minutes, 32 seconds
    

    You can also skip address substitution if name is unknown. Second while loop will be:

    while read -a LINE; do
        ADDR="${LINE[1]%,}"
        [ -z "$ADDR" ] && continue
        NAME="${HOSTMAP[$ADDR]}"
        [ -n "$NAME" ] && LINE[1]="$NAME,"
        echo ${LINE[@]}
    done < clients.txt
    
    Login or Signup to reply.
  3. FWIW, once you pull awk into the mix you can usually eliminate the need for other tools like cat, (e)grep, cut, tr, sed, and in this case xargs. awk is also quite useful when trying to join/match different files. Net result: OP’s entire process can probably be performed with a single awk script (which in turn is going to be more efficient than the current method).

    OP hasn’t (yet) provided a copy of wghub.conf so I’ve reverse engineered OP’s code to come up with a file which, when fed to OP’s cat/grep/egrep/awk/cut/tr/xargs pipeline, will generate the same set of hostname/ip pairs:

    $ cat wghub.conf
    .conf      _____         samsung/__________
    .conf      configuration
    AllowedIPs _____         10.88.188.11/__________
    .conf      _____         ultra/__________
    .conf      configuration
    AllowedIPs _____         10.88.188.12/__________
    .conf      _____         mi10/__________
    .conf      configuration
    AllowedIPs _____         10.88.188.13/__________
    .conf      _____         drunksorrow/__________
    .conf      configuration
    AllowedIPs _____         10.88.188.15/__________
    .conf      _____         win11/__________
    .conf      configuration
    AllowedIPs _____         10.88.188.16/__________
    .conf      _____         jderu/__________
    .conf      configuration
    AllowedIPs _____         10.88.188.17/__________
    
    $ cat wghub.conf | egrep '.conf|AllowedIPs' | grep -v configuration | awk '{print $3}'|cut -d "/" -f1|tr "n" " "|xargs -n 2
    samsung 10.88.188.11
    ultra 10.88.188.12
    mi10 10.88.188.13
    drunksorrow 10.88.188.15
    win11 10.88.188.16
    jderu 10.88.188.17
    

    I’ve also added a bogus line to OP’s crontab output to demonstrate leaving an ip address alone if there is no match in wghub.conf

    $ cat crontab.out
    Client: 10.88.188.13, Latest Handshake: 1 minute, 42 seconds
    Client: 10.88.188.16, Latest Handshake: 51 minutes, 32 seconds
    Client: 99.99.99.99, Latest Handshake: 99 minutes, 99 seconds
    

    One awk approach to replacing all of OP’s current code that also performs the match with the crontab output:

    awk '
    FNR==NR { if ( $0 ~ /.conf|AllowedIPs/ ) {      # 1st file: match lines on ".conf|AllowedIPs"
                 if ( $0 ~ /configuration/ )        # if there is an additional match on "configuration" then ...
                    next                            # skip to next input line from 1st file
    
                 sub(//.*/,"",$3)                  # strip trailing "/..." from 3rd field
    
                 if (name) { ip_name[$3] = name     # if name is populated then add entry to array ($3==ip at this point) and ...
                             name = ""              # clear name
                           }
                 else      { name = $3 }            # else save 3rd field as name
              }
              next                                  # skip to next input line from 1st file
            }
    
            { ip = $2                               # 2nd file: make copy of 2nd field
              sub(/,$/,"",ip)                       # strip trailing "," from ip
    
              if (ip in ip_name)                    # if ip is an index in the ip_name[] array then ...
                 sub(ip,ip_name[ip])                # replace ip (in current line) with contents of ip_name[ip]
            }
    1                                               # 2nd file: print current line
    ' wghub.conf crontab.out
    

    This generates:

    Client: mi10, Latest Handshake: 1 minute, 42 seconds
    Client: win11, Latest Handshake: 51 minutes, 32 seconds
    Client: 99.99.99.99, Latest Handshake: 99 minutes, 99 seconds
    

    NOTES:

    • the title mentions I want to replace something in a file but it’s not clear (to me) if OP wants to update the file containing the crontab output
    • if OP has access to GNU awk (aka gawk) then it would be possible to use the -i inplace feature to update the crontab file; OP can read up on that and ask a new question if there are issues getting it to work (it’s a bit more involved when you only want to update some of the files fed to awk)
    • the easiest/quickest coding solution would be to direct the awk output to a tmpfile, (optional) make a backup copy of crontab.out, then cp tmpfile crontab.out
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search