Home > front end >  How to do operations depending on the presence of a specific string in bash?
How to do operations depending on the presence of a specific string in bash?

Time:07-01

I am working with a csv file, so imagine I have this column:

5;10;>11;20;<14

My desired output would be:

5;10;12;20;13

So I would like to add 1 to those values who have the greater than (>) symbol and to subtract 1 to those values with a lesser than (<) symbol with bash language. I have tried something weird with sed but given that it interprets those changes as strings it didn't work out.

Any suggestions?

CodePudding user response:

With awk (tested with GNU awk):

$ awk -F\; -v OFS=\; '
{
  for(i = 1; i <= NF; i  ) {
    if($i ~ /^<[[:digit:]] $/) {
      sub(/^</,"",$i)
      $i--
    }
    else if($i ~ /^>[[:digit:]] $/) {
      sub(/^>/,"",$i)
      $i  
    }
  }
} 1' <<< "5;10;>11;20;<14"
5;10;12;20;13

Warning: use the following if and only if you trust your input file and you are 100% sure it does not contains malicious fields (see the final note).

With GNU sed (and assuming your shell is bash), a bit shorter but also a bit more difficult to understand (as usual with sed):

$ sed -E '
s/<([[:digit:]] )/$((\1-1))/g
s/>([[:digit:]] )/$((\1 1))/g
s/.*/printf "%s\n" "&"/e
' <<< "5;10;>11;20;<14"
5;10;12;20;13

That is (where N is a string of digits), substitute all <N with $((N-1)), all >N with $((N 1)), substitute the resulting string S with printf "%s\n" "S", execute it with bash and replace with the output (this is what the e modifier of the substitute command does). In your example the input string successively becomes:

5;10;>11;20;$((14-1))
5;10;$((11 1));20;$((14-1))
printf "%s\n" "5;10;$((11 1));20;$((14-1))"
5;10;12;20;13

The reason why there is a serious security issue here is that if one of your fields is, for instance, $(rm -rf ~/*) it will simply and recursively delete your entire home directory... So, if you do not control the input prefer the awk version.

CodePudding user response:

5;10;>11;20;<14

|

{m,g}awk '
BEGIN {
    _*=(OFS= "") (__-=_^= FS ="("(\
   ___="\31\17")"|"(____="\16\24")") "
} {
    gsub(";[<>][0-9] ",____ "&" ___)
    gsub(____ ";[<>]", "&" ___)
    NF
    for(_ =(_^=($_=$_)<"") _;_<=NF;_  ) {
        if ($_~"^[0-9] $") {
            $_ =__^($(_ __)~"[<]$")
        }
    } print $(_=_<_) }'

=

5;10;>12;20;<13
  • Related