Home > Blockchain >  Bash strings getting cut off when outputting from array at space marks
Bash strings getting cut off when outputting from array at space marks

Time:04-10

So I am going through a log file and extracting information from said log file such as device name, location, and time of log. It looks something like the following:

edge2.mco1-9 Jun 11:32:24 GMT %SEC_LOGIN-5-LOGIN_SUCCESS: Login Success

I am coming across an issue where when I run the following:


for y in ${DeviceName[@]}
do
    echo "Device: $y"
    counter=1
    for z in ${LocationName[@]}
    do
        testing=($(grep "$y\.$z\-.*\%" hacker.txt | cut -f2 -d "-" | cut -f1 -d% | head -n 1))
        if [[ ${testing[$counter]} =~ [^0-9] ]]
        then
            echo $z
            echo $testing
        fi
        
    done
    
done

the day number is the only part outputted. DeviceName is an array of device names. LocationName is an array of location names. The complicated part was due to me wanting to only output the location name if it had something coming from the grep since I did not want a long list of locations that had no time attached to it. I did experiment with saving to a .txt file, which did do the correct time, but it seems outputting the correct time is more troublesome due to spaces. Using the example,

9 Jun 11:32:24 GMT

should be the output. Is there any way to get around this? The $testing is kept as attempts to do ${testing[$counter]}, $(${testing[$counter]}), and other methods did not work giving me errors. Errors are Jun and ./finder: line 113: Jun: command not found respectively $testing at least gives me the day number. Obviously there is an issue with spaces but I don't know how to fix this.

CodePudding user response:

Without a complete set of input data (eg, contents of DeviceName[] and LocationName[] arrays) we'll have to make some assumptions ...

From the given sample input line:

line='edge2.mco1-9 Jun 11:32:24 GMT %SEC_LOGIN-5-LOGIN_SUCCESS: Login Success'

We'll pick the following values for the for loop variables:

y=edge2
z=mco1

A modified grep command:

$ grep "$y\.$z\-.*\%" <<< "${line}" | cut -f2 -d "-" | cut -f1 -d% | head -n 1                                                                    
9 Jun 11:32:24 GMT

Loading this into the testing[] array:

$ testing=( $(grep "$y\.$z\-.*\%" <<< "${line}" | cut -f2 -d "-" | cut -f1 -d% | head -n 1) )
$ typeset -p testing
declare -a testing=([0]="9" [1]="Jun" [2]="11:32:24" [3]="GMT")

Running the test:

$ counter=1
$ [[ ${testing[$counter]} =~ [^0-9] ]] && echo "success"
success

# turn on debugging to see what we're testing:
$ set -xv                                                  # turn on debugging

$ [[ ${testing[$counter]} =~ [^0-9] ]] && echo "success"
[[ ${testing[$counter]} =~ [^0-9] ]] && echo "success"
  [[ Jun =~ [^0-9] ]]                                      # contents of testing[1] == 'Jun'
  echo success
success

$ set  xv                                                  # turn off debugging

So far so good.

OP has stated the desired output should look like:

9 Jun 11:32:24 GMT

But the current echo command has an issue:

echo $testing                                            

$testing is an array; without an index bash treats $testing the same as ${testing[0]} which in this case outputs 9 (see the output from typeset -p testing - above); to output the entire contents of the array OP can use:

$ echo "${testing[@]}"
9 Jun 11:32:24 GMT

Net result: OP should be able to change echo $testing to echo "${testing[@]}" to obtain the desired output.


OP has mentioned the code complication (loading testing[] array, comparing testing[1] ???) is due to only wanting to print output if the grep found a match.

We can streamline the code a bit by replacing the guts of the for z loop with:

    # no need for array, just capture datetime stamp:

    dtstamp=$(grep "$y\.$z\-.*\%" hacker.txt | cut -f2 -d "-" | cut -f1 -d% | head -n 1)

    # is length of 'dtstamp' is non-zero?

    if [[ -n "${dtstamp}" ]]
    then
        echo "${z}"
        echo "${dtstamp}"
    fi

If the objective is to only print ${z} if the grep finds a match (ie, OP doesn't need to print ${dtstamp}) we can streamline the code a bit more to where the only command in the for z loop is:

grep "$y\.$z\-.*\%" hacker.txt >/dev/null && echo "${z}"

Reviving my test code:

$ z=mco1
$ grep "$y\.$z\-.*\%" <<< "${line}" >/dev/null && echo "${z}"
mco1

$ z=xxxx
$ grep "$y\.$z\-.*\%" <<< "${line}" >/dev/null && echo "${z}"
              <<<=== no output

NOTES:

  • can also remove the counter=1 from the current code
  • OP should consider cutting-n-pasting their code (along with the appropriate shebang, eg, #!/bin/bash) into shellcheck.net; this will report on syntax issues as well as make some suggestions re: 'best practices'
  • Related