JamesPoppycock JamesPoppycock - 3 months ago 15
Bash Question

Using awk in a wrong approach

I am told I used awk in a wrong approach in the below code, but I am dumbfounded as to how to improve my code so that it is more simpler to read.

read -r bookName
read -r authorName

if grep -iqx "$bookName:$authorName" cutText.txt
then
lineNum=`awk -v bookName="$bookName" -v authorName="$authorName" '$0 ~ bookName ":" authorName {print NR} BEGIN{IGNORECASE=1}' BookDB.txt`

echo "Enter a new title"
read -r newTitle

awk -F":" -v bookName="$bookName" -v newTitle="$newTitle" -v lineNum="$lineNum" 'NR==lineNum{gsub(bookName, newTitle)}1' cutText.txt > temp2.txt
mv -f temp2.txt cutText.txt
else
echo "Error"
fi


My cutText.txt contains content as shown below:

Hairy Potter:Rihanna
MARY IS A LITTLE LAMB:Kenny
Sing along:May


This program basically update a new title in cutText.txt. If a user wants to change
MARY IS A LITTLE LAMB
to
Mary is not a lamb
, he will enter the new title and
cutText.txt
will replace the original title with
Mary is not a lamb
.

A problem arises now that if a user enter "Mary is a little lamb" for
$newTitle
, this code of works just doesn't work, because it does take the case into account.
It will only work is user types "MARY IS A LITTLE LAMB". I came to be aware that
BEGIN{IGNORECASE=1}
is gawk-sepcific, therefore it cannot be used in awk.

How can I script this better so I can ignore case in user input? Thank you!

Answer

To get you started. Create files

r.awk

function asplit(str, arr, sep,   temp, i, n) {  # make an assoc array from str
    n = split(str, temp, sep)
    for (i = 1; i <= n; i++)
        arr[temp[i]]++
    return n
}

function regexpify(s,   back, quote, rest, all, meta, n, c, u, l, ans) { 
    back = "\\"; quote = "\"";
    rest = "^$.[]|()*+?"
    all  = back quote rest
    asplit(all, meta, "")

    n = length(s)
    for (i=1; i<=n; i++) {
    c = substr(s, i, 1)
    if      (c in meta)
        ans = ans back c
    else if ((u = toupper(c)) != (l = tolower(c)))
        ans = ans "[" l u "]"
    else
        ans = ans c
    }

    return ans
}

BEGIN {
    old = regexpify(old)
    sep = ":"; m = length(sep)
}

NR == n {
    i = index($0, sep)
    fst = substr($0,   1, i-m)
    scn = substr($0, i+m     )

    gsub(old, new, fst)
    print fst sep scn

    next
}

{
    print
}

cutText.txt

Hairy Potter:Rihanna
MARY IS A LITTLE LAMB:Kenny
Sing along:May

Usage:

awk -v n=2 -v old="MArY iS A LIttLE lAmb" -v new="Mary is not a lamb" -f r.awk  cutText.txt 

Expected output:

Hairy Potter:Rihanna
Mary is not a lamb:Kenny
Sing along:May