'Using awk output for conditional statement

I have some data with the following format:

 2     1  500  500  500
 3     1  500  500  500
 6     1  500  500  500
 8     1  500  500  500
 9     1  500  500  500
11     1  500  500  500
12     1  500  500  500
14     1  500  500  500
15     1  500  500  500
16     1  500  500  500
17     1  500  500  500
20     1  500  500  500
21     1  500  500  500
23     1  500  500  500
24     1  500  500  500
25     1  500  500  500
27     1  500  500  500
30     1  500  500  500
31     1  500  500  500
32     1  500  500  500
33     1  500  500  500
34     1  500  500  500
35     1  500  500  500
38     1  500  500  500
40     1  500  500  500
41     1  500  500  500
43     1  500  500  500
44     1  500  500  500
46     1  500  500  500
47     1  500  500  500

I want to change the 500 values to 100 only in the lines in which the 1st column equal from 11-40. For now I'm doing something like:

Numbers=($(seq 11 1 40))
File=filename.txt
for i in ${Numbers[*]}
do
        if [ $i == awk '{print $1}' $File ];then
        NumberLine=$(grep -n $i $File | cut -d : -f 1)
        sed -i "${NumberLine}s/500/100/" $File
        fi
done

Individually, each line seems to do what I want to do, but when I put them in them in the loop, I get the following error:

./changeRestraints.sh: line 5: [: too many arguments

I suspect that this has to do with my awk as a part of my conditional statement. How can I fix this to make this script run?

Thank you,



Solution 1:[1]

It is not a good idea to invoke awk and sed in the loop repeatedly wrt efficiency. Please try:

awk '$1>10 && $1<=40 {gsub(/\<500\>/, "100")} 1' filename.txt

Please note the regex \< and \> are GNU awk extension which match word boundaries.

[EDIT]
The gsub() function is a variant of sub() which replaces multiple occurences of the matched strings, while sub() replaces the first match only. The relationship between sub() and gsub() is similar to that of s/regex/repl/ and s/regex/repl/g in sed.

If you want to use variables for the embedded numbers, you can make use of -v varname=value mechanism which can assign awk variables via the command line option:

#!/bin/bash

start=11    # bash variables
stop=40
from=500
to=100
awk -v start="$start" -v stop="$stop" -v from="$from" -v to="$to" '$1>=start && $1<=stop {gsub("\\<" from "\\>", to)} 1' filename.txt
  • When assiging -v start="$start", the lhs start is an awk variable name and the rhs "$start" is a bash variable. We can use the same name for them (although they look confusing). You can also assign the variable with an immediate value such as -v start=11.
  • As we cannot use the regex quoting /regex/ to include an awk variable, we need to say "\\<" from "\\>" instead. The whitespaces in between are just for readability and it is equilalent to "\\<"from"\\>".

Solution 2:[2]

You never actually execute awk. You compare $i to the string awk, and then the test command finds additional arguments which it can't handle. Therefore you get the too many arguments error.

You need to run awk in order to get its output, for instance by doing a

if [ "$i" = "$(awk ... )" ]
then
  ...

Solution 3:[3]

You could also use sed, where the pattern ^(40|[23][0-9]|1[1-9])[[:space:]] matches a number 11-40 followed by a space at the start of the string.

sed -E '/^(40|[23][0-9]|1[1-9])[[:space:]]/s/500/100/g' file

Output

 2     1  500  500  500
 3     1  500  500  500
 6     1  500  500  500
 8     1  500  500  500
 9     1  500  500  500
11     1  100  100  100
12     1  100  100  100
14     1  100  100  100
15     1  100  100  100
16     1  100  100  100
17     1  100  100  100
20     1  100  100  100
21     1  100  100  100
23     1  100  100  100
24     1  100  100  100
25     1  100  100  100
27     1  100  100  100
30     1  100  100  100
31     1  100  100  100
32     1  100  100  100
33     1  100  100  100
34     1  100  100  100
35     1  100  100  100
38     1  100  100  100
40     1  100  100  100
41     1  500  500  500
43     1  500  500  500
44     1  500  500  500
46     1  500  500  500
47     1  500  500  500

Solution 4:[4]

Another way to do it in gawk:

gawk '$1~/1[1-9]|[23][0-9]|40/{ $3=$4=$5=100 }1' file

when you want to maintain the spaces:

gawk '$1~/1[1-9]|[23][0-9]|40/{ gsub(/\<500\>/,"100") }1' file

Solution 5:[5]

with sed

sed -i '/^11/,/^40/s/500/100/g' filename.txt

Solution 6:[6]

maybe something like this :

mawk 'gsub("500", (_=+$1)<11 || (40<_) ? "&" : "100")'

 2     1  500  500  500
 3     1  500  500  500
 6     1  500  500  500
 8     1  500  500  500
 9     1  500  500  500
11     1  100  100  100
12     1  100  100  100
14     1  100  100  100
15     1  100  100  100
16     1  100  100  100
17     1  100  100  100
20     1  100  100  100
21     1  100  100  100
23     1  100  100  100
24     1  100  100  100
25     1  100  100  100
27     1  100  100  100
30     1  100  100  100
31     1  100  100  100
32     1  100  100  100
33     1  100  100  100
34     1  100  100  100
35     1  100  100  100
38     1  100  100  100
40     1  100  100  100
41     1  500  500  500
43     1  500  500  500
44     1  500  500  500
46     1  500  500  500
47     1  500  500  500

if you prefer a FS + OFS based approach, then

 mawk 'NF *= (OFS = (_=+$1)<11||(40<_) ? FS : __)^!__' FS='500' __='100'

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 user1934428
Solution 3 The fourth bird
Solution 4 Luuk
Solution 5 ufopilot
Solution 6 RARE Kpop Manifesto