lightsnail lightsnail - 2 months ago 15
Bash Question

Extract specific characters from a string in a loop in linux

This is a new and further question based on:
Output the result of each loop in different columns.

But since it is a new question, you don't need to look at the question in link, I will clearly clarify the new question below.

money.txt
file has two columns: (name and money)

Mary 13
Lucy 8
Jack 20


range.txt
file has three columns: (fruit, min_value and max_value)

apple 10 15
banana 7 12
orange 17 22
blueberry 14 22


My aim is to test whether the money in
money.txt
file is between the min_value and max_value in
range.txt
. If yes, print out the (
max_value
-
money
)th character of
fruit
in
range.txt
, If not, output
"x"
.

For example,
Mary
is
13
in the
money.txt
,
13
is within the min_value and max_value of
apple
, and the
max_value
-
money
value is
15
-
13
=
2
, so it should print out the 2nd character of
apple
, that is
p
.

The expected result is : (The 4th column is for Mary, the 5th column is for Lucy, and the 6th column is for Jack)

apple 10 15 p x x
banana 7 12 x a x
orange 17 22 x x r
blueberry 14 22 x x l


With @ocurran 's help, I tried:

# load prices by index to maintain read order
awk 'FNR == NR {
money[names++]=$2
next
}
# save max index to avoid using non-standard length(array)
END {
names=NR
}
{
l = $1 " " $2 " " $3
for (i=0; i < names; i++) {
if ($2 <= money[i] && $3 >= money[i]) {
fruit=$1
fruitcharacter=${fruit:($3-money[i]-1):1}
l = l " " $fruitcharacter
} else {
l = l " x"
}
}
print l
}' money.txt range.txt


The result showed that:

awk: line 14: syntax error at or near {
awk: line 16: syntax error at or near else
awk: line 19: syntax error at or near }


It seems that
fruitcharacter=${fruit:($3-money[i]-1):1}
can not work. But as I know, we can use
${string: index: length}
to extract the character of a string, I don't know why it can not work at this situation. Can you help me to solve this problem. Thank you.

Answer
$ cat tst.awk
NR==FNR { money[NR]=$2; next }
{
    out = $0
    for (i=1; i in money; i++) {
        out = out OFS ( (money[i]>=$2) && (money[i]<=$3) ? substr($1,2,1) : "x" )
    }
    print out
}

$ awk -f tst.awk money.txt range.txt
apple 10 15 p x x
banana 7 12 x a x
orange 17 22 x x r
blueberry 14 22 x x l

and if you want some column headers and nicer output formatting:

$ cat tst.awk
NR==FNR { names[NR]=$1; money[NR]=$2; next }
FNR==1 {
    out = "Fruit" OFS "Min" OFS "Max"
    for (i=1; i in names; i++) {
        out = out OFS names[i]
    }
    print out
}
{
    out = $0
    for (i=1; i in money; i++) {
        out = out OFS ( (money[i]>=$2) && (money[i]<=$3) ? substr($1,2,1) : "x" )
    }
    print out
}

$ awk -f tst.awk money.txt range.txt | column -t
Fruit      Min  Max  Mary  Lucy  Jack
apple      10   15   p     x     x
banana     7    12   x     a     x
orange     17   22   x     x     r
blueberry  14   22   x     x     l