0

I've found a weird problem I cannot resolve.

I need to extract some values in a variable, these values are after a string.

The name of the variable in this example is: DSLSTATE

Here an example of the value in it:

NewEnable 1
NewStatus Up
NewDataPath Fast
NewUpstreamCurrRate 21598
NewDownstreamCurrRate 170788
NewUpstreamMaxRate 25692
NewDownstreamMaxRate 170788
NewUpstreamNoiseMargin 90
NewDownstreamNoiseMargin 60
NewUpstreamAttenuation 150
NewDownstreamAttenuation 170
NewATURVendor 41564d00
NewATURCountry 0400
NewUpstreamPower 486
NewDownstreamPower 514

This is not an array.

To extract the value I want I make:

ATTUPSTREAM=$(echo "$DSLTATE" | awk '{for (I=1;I<NF;I++) if ($I == "NewUpstreamAttenuation") print $(I+1)}')

In this example the value should be 150 but it's always empty.

The weird thing is that I can extract all the values from the same variable with the same command with the exception of the one of the example. If I execute the same command on terminal it works, in the script is not working.

Here an extract of the script:

UPSTREAM=$(echo $DSLSTATE | awk '{for (I=1;I<NF;I++) if ($I == "NewUpstreamCurrRate") print $(I+1)}')
DOWNSTREAM=$(echo $DSLSTATE | awk '{for (I=1;I<NF;I++) if ($I == "NewDownstreamCurrRate") print $(I+1)}')
ATTUPSTREAM=$(echo $DSLTATE | awk '{for (I=1;I<NF;I++) if ($I == "NewUpstreamAttenuation") print $(I+1)}')
ATTDOWNSTREAM=$(echo $DSLSTATE | awk '{for (I=1;I<NF;I++) if ($I == "NewDownstreamAttenuation") print $(I+1)}')
SNRUPSTREAM=$(echo $DSLSTATE | awk '{for (I=1;I<NF;I++) if ($I == "NewUpstreamNoiseMargin") print $(I+1)}')
SNRDOWNSTREAM=$(echo $DSLSTATE | awk '{for (I=1;I<NF;I++) if ($I == "NewDownstreamNoiseMargin") print $(I+1)}')

These are the results:

ATTUPSTREAM=
ATTDOWNSTREAM=170
SNRUPSTREAM=40
SNRDOWNSTREAM=50

Maybe there is some strange character in the DSLSTATE variable in the spacing between the values?

Hoping someone can enlighten me.

Jens
  • 69,818
  • 15
  • 125
  • 179
SimoneM
  • 303
  • 1
  • 9
  • Why don't you just do `ATTUPSTREAM=$(awk '/NewUpstreamAttenuation/{print $2}' <<< $DSLTATE)`? This will work for all the other values as well if you replace the search string with the respective one. – accdias Apr 30 '21 at 12:40
  • 3
    Don't use all upper case names for awk or non-exported shell variables to avoid clashing with builtin variables and other reasons, e.g. see https://stackoverflow.com/questions/673055/correct-bash-and-shell-script-variable-capitalization for the shell issues. – Ed Morton Apr 30 '21 at 12:46
  • It was a typo in my comment. – accdias Apr 30 '21 at 12:58
  • accdias that typo is in the OPs code too - I strongly suspect @rowboat nailed the issue. – Ed Morton Apr 30 '21 at 12:58
  • Oh! Good catch! – accdias Apr 30 '21 at 13:03
  • Thank you all guys and thank you all for the answers! My mistake here, @rowboat you are right and that is the error now it works! That is the result of my poor coding as EdMorton pointed out – SimoneM Apr 30 '21 at 13:03
  • @EdMorton, since you are the master of shell scripting around here (and I really mean it), do you have any comments about my suggestion in my comment above? Your opinion is always really appreciated. – accdias Apr 30 '21 at 13:05
  • 2
    @accdias I'm hardly the master of shell scripting but here's my feedback FWIW - it's using a non-portable stringizing construct `<<<` and the OP hasn't told us which shell they''re using so that may not work for them, and it'd fail if a tag was part of another tag, e.g. imagine you had both `NewUpstreamAttenuation` and `NewUpstreamAttenuationMax` in the input - that script would print both values, and (probably less likely) if any of the tags contained a regexp metachar, e.g. `The.Max`, then you'd need to make sure to escape them. – Ed Morton Apr 30 '21 at 13:11

2 Answers2

3
  1. Don't use all upper case names for awk or non-exported shell variables to avoid clashing with builtin variables and other reasons, e.g. see Correct Bash and shell script variable capitalization for the shell issues.

  2. You don't need to loop looking for a tag (name) when the input is always in tag-value pairs.

  3. When you have common code put it in a function, don't repeat it multiple times - for all we know there could be a control char or other issue in the code for that one tags value.

Start by changing your code to use a simple lookup function:

$ tag2val() { echo "$DSLSTATE" | awk -v tag="$1" '$1==tag { print $2 }'; }

$ tag2val NewDataPath
Fast

$ tag2val NewUpstreamAttenuation
150

and then let us know if you still have a problem. If you do then edit your question to show the output of echo "$DSLSTATE" | cat -Ev.

Ed Morton
  • 188,023
  • 17
  • 78
  • 185
  • 1
    Hi and thanks you are right for my poor coding here...multiple repetitive code is a bad attitude I will change the code. – SimoneM Apr 30 '21 at 13:05
  • I still have trouble even with the functions with the print $2 part of the awk. It seems that there is some difference between variables ...That is the main reason I used the awk with the cycle in the first place. The main problem is when echo consider the value as a one only row and when a list – SimoneM Apr 30 '21 at 14:14
  • `echo` doesn't do that. Either there are no newlines in your variable or, more likely given the code in your question, you're removing the double quotes from `echo "$DSLSTATE"` and so, among other things, asking the shell to pass every string of non-blanks in `$DSLSTATE` to `echo` as separate arguments instead of one single argument. It's extremely important to understand quoting in shell - see https://mywiki.wooledge.org/Quotes. – Ed Morton Apr 30 '21 at 15:04
0

If you are using Bash, another option can be converting DSLSTATE into an associative array. After that, it will be easier to get a value based on a key:

#!/bin/bash

DSLSTATE='NewEnable 1
NewStatus Up
NewDataPath Fast
NewUpstreamCurrRate 21598
NewDownstreamCurrRate 170788
NewUpstreamMaxRate 25692
NewDownstreamMaxRate 170788
NewUpstreamNoiseMargin 90
NewDownstreamNoiseMargin 60
NewUpstreamAttenuation 150
NewDownstreamAttenuation 170
NewATURVendor 41564d00
NewATURCountry 0400
NewUpstreamPower 486
NewDownstreamPower 514'

declare -A kv

while read k v; do
    kv[$k]=$v
done <<< $DSLSTATE

echo "NewDownstreamPower: ${kv[NewDownstreamPower]}"

The code above will result in:

NewDownstreamPower: 514
accdias
  • 5,160
  • 3
  • 19
  • 31