Bash 以错误的方式使用awk

Bash 以错误的方式使用awk,bash,shell,awk,Bash,Shell,Awk,我被告知我在下面的代码中以错误的方式使用了awk,但对于如何改进我的代码以使其更易于阅读,我感到目瞪口呆 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

我被告知我在下面的代码中以错误的方式使用了awk,但对于如何改进我的代码以使其更易于阅读,我感到目瞪口呆

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包含如下内容:

Hairy Potter:Rihanna
MARY IS A LITTLE LAMB:Kenny
Sing along:May
该程序基本上更新了cutText.txt中的新标题。如果用户想将
MARY IS a LITTLE LAMB
更改为
MARY not a LAMB
,他将输入新标题,
cutText.txt
将用
MARY not a LAMB
替换原始标题

现在出现了一个问题,如果用户在
$newTitle
中输入“Mary是一只小羊羔”,那么这个工作代码就不起作用了,因为它考虑到了这个情况。 只有当用户输入“MARY是一只小羊羔”时,它才会起作用。我开始意识到
BEGIN{IGNORECASE=1}
是gawk sepcific,因此不能在awk中使用


如何更好地编写脚本,以便在用户输入中忽略大小写?谢谢大家!

好了,伙计们,我刚意识到我是个笨蛋****

我一整天都在扯头发,我所要做的就是这么做

lineNum=`grep -in "$bookName:$authorName" BookDB.txt | cut -f1 -d":"`

sed -i "${lineNum}s/$bookName/$newTitle/I" BookDB.txt cutText.txt

天哪,我真想自杀。

好了,伙计们,我刚意识到我也是个哑巴****

我一整天都在扯头发,我所要做的就是这么做

lineNum=`grep -in "$bookName:$authorName" BookDB.txt | cut -f1 -d":"`

sed -i "${lineNum}s/$bookName/$newTitle/I" BookDB.txt cutText.txt

天哪,我真想自杀。

让你开始吧。创建文件

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
}
用法:

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

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

让你开始。创建文件

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
}
用法:

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

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

这将使用精确的字符串匹配,因此在部分匹配时,或者如果旧标题包含
或regexp元字符,或者如果新标题包含反引用(例如
&
),或者如果反斜杠(
\
)出现在迄今为止其他脚本将失败的任何字段或任何其他情况中:

$ cat tst.sh
read -r oldTitle
read -r authorName

echo "Enter a new title"
read -r newTitle

awk '
BEGIN {
    ot=ARGV[1]; nt=ARGV[2]; an=ARGV[3]
    ARGV[1] = ARGV[2] = ARGV[3] = ""
}
tolower($0) == tolower(ot":"an) {
     $0 = nt":"an
     found = 1
}
{ print }
END {
    if ( !found ) {
        print "Error" | "cat>&2"
    }
}
' "$oldTitle" "$newTitle" "$authorName" cutText.txt > temp2.txt &&
mv -f temp2.txt cutText.txt

我正在从ARGV[]填充awk变量,因为如果我在arg列表中使用
-v var=val
var=val
填充它们,那么任何反斜杠都将被解释,因此,例如
\t
,将成为文本制表符。请参阅我很久以前写的shell常见问题解答文章-

顺便说一句,我把
书名
改为
旧标题
,因为这似乎对
新标题
更有意义。没有功能上的差异

在进行任何文本操作时,了解字符串和各种regexp风格(BREs/EREs/PCREs)之间的差异以及部分匹配和完全匹配之间的差异是非常重要的

  • 默认情况下,grep在BRE上运行,在ERE上运行的是
    -E
    arg,在PCRE上运行的是
    -P
    arg,在字符串上运行的是
    -F
    arg
  • 默认情况下,sed在BRE上运行,在给定
    -E
    参数的ERE上运行。sed不支持PCREs。sed也不能对字符串进行操作,要使regexp表现得像字符串一样令人痛苦,请参阅
  • 默认情况下,awk操作ERE和字符串。您只需将ERE与regexp运算符和字符串与字符串运算符一起使用(请参阅)

  • 因此,如果像您的情况一样,您需要对文本中的所有字符进行逐字处理,那么这是一个字符串,而不是regexp,因此您不应该在其上使用sed,如果您希望在文件中快速找到字符串并对部分匹配感到满意,您应该使用grep,但是,如果您想做除此之外的任何事情,如更改文件中的字符串或进行精确匹配,则应使用awk。

    这使用精确的字符串匹配,因此在部分匹配或旧标题包含
    或regexp元字符,或者如果新标题包含反向引用,则不能失败(例如,
    &
    )或如果任何字段中出现反斜杠(
    \
    ),或您的其他脚本到目前为止将失败的任何其他情况:

    $ cat tst.sh
    read -r oldTitle
    read -r authorName
    
    echo "Enter a new title"
    read -r newTitle
    
    awk '
    BEGIN {
        ot=ARGV[1]; nt=ARGV[2]; an=ARGV[3]
        ARGV[1] = ARGV[2] = ARGV[3] = ""
    }
    tolower($0) == tolower(ot":"an) {
         $0 = nt":"an
         found = 1
    }
    { print }
    END {
        if ( !found ) {
            print "Error" | "cat>&2"
        }
    }
    ' "$oldTitle" "$newTitle" "$authorName" cutText.txt > temp2.txt &&
    mv -f temp2.txt cutText.txt
    

    我从ARGV[]填充awk变量,因为如果我在arg列表中使用
    -v var=val
    var=val
    填充它们,那么任何反斜杠都会被解释,因此
    \t
    将成为一个文本制表符。请参阅我很久以前写的shell FAQ文章-

    顺便说一句,我把
    bookName
    改成了
    oldTitle
    ,因为这似乎对
    newTitle
    更有意义。没有功能上的区别

    在进行任何文本操作时,了解字符串和各种regexp风格(BREs/EREs/PCREs)之间的差异以及部分匹配和完全匹配之间的差异是非常重要的

  • 默认情况下,grep在BRE上运行,在ERE上运行的是
    -E
    arg,在PCRE上运行的是
    -P
    arg,在字符串上运行的是
    -F
    arg
  • sed默认在BRE上操作,在ERE上使用
    -E
    参数。sed不支持PCREs。sed也不能在字符串上操作,要使regexp表现得像字符串一样令人痛苦,请参阅
  • 默认情况下,awk操作ERE和字符串。您只需将ERE与regexp运算符和字符串与字符串运算符一起使用(请参阅)
  • 因此,如果像您的情况一样,您需要对文本中的所有字符进行逐字处理,那么这是一个字符串,而不是regexp,因此您不应该在其上使用sed。如果您想在文件中快速找到字符串并对部分匹配感到满意,您应该使用grep,但如果您想做任何事情,例如更改字符串在文件中或进行精确匹配时,您应该使用awk。

    您的意思可能是,如果用户输入“Mary is a little lamb”(玛丽是一只小羊羔),那么现在会出现问题对于$bookName。无论如何,这是一个
    awk
    唯一的问题。您可能应该删除
    bash
    shell
    标记。并且您可能应该将问题简化为如何告诉非GNU awk忽略模式中的大小写?,并提供一个小示例,说明您有什么行为以及您想要什么行为。说明您的版本属于
    awk