'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 lhsstartis 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 |
