2016-08-13 17 views
0

我被告知我在下面的代碼中以錯誤的方式使用了awk,但是我對如何提高我的代碼以便讀取更加簡單感到驚訝。在錯誤的方法中使用awk

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

這個程序基本上更新cutText.txt新標題:如下圖所示

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 

我cutText.txt包含的內容。如果用戶想要將MARY IS A LITTLE LAMB更改爲Mary is not a lamb,他將輸入新標題,cutText.txt將用Mary is not a lamb替換原標題。

現在出現的一個問題是,如果用戶輸入「Mary是一隻小羊羔」作爲$newTitle,則此作品代碼不起作用,因爲它確實考慮了這種情況。 它只會工作是用戶類型「瑪麗是一個小LAMB」。我開始意識到BEGIN{IGNORECASE=1}是gawk-sepcific,因此它不能在awk中使用。

如何更好地編寫腳本以便我可以忽略用戶輸入中的情況?謝謝!

+1

你大概意思_A出現的問題是,如果用戶輸入「瑪麗是隻小羊羔」爲$ bookName_。無論如何,這只是一個awk唯一的問題。你應該刪除'bash'和'shell'標籤。你應該簡化你的問題,如_如何告訴一個非GNU awk忽略模式中的大小寫_ _,以及你想要什麼樣的行爲和你想要的行爲的一個小例子。指出你使用的是什麼版本的'awk'將是一個加號。 –

+0

好的,謝謝你的提示!我仍然對編碼不熟悉>< – JamesPoppycock

+0

讓我們先讓它工作健壯,然後再擔心「稍後閱讀」;-)。您當前的代碼在書名或作者姓名中的部分匹配,regexp元字符,轉義字符,反向引用,冒號等方面會以各種方式失敗,並且如果awk腳本中發生錯誤,將會擦除數據庫。 –

回答

1

這將使用精確的字符串匹配,因此不能部分匹配失敗或者您的舊標題包含:或正則表達式元字符,或者如果新標題包含反向引用(例如&),或者如果一個反斜槓(\)出現在任何領域或任何你的其它腳本日期將失敗的其他情況:

$ 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 

$ cat cutText.txt 
Hairy Potter:Rihanna 
MARY IS A LITTLE LAMB:Kenny 
Sing along:May 

$ ./tst.sh 
mary is a little lamb 
kenny 
Enter a new title 
Mary is not a lamb 

$ cat cutText.txt 
Hairy Potter:Rihanna 
Mary is not a lamb:kenny 
Sing along:May 

我填充awk的變量從ARGV [],因爲如果我使用的ARG列表中,則任何反斜槓將被解釋-v var=valvar=val\t填充它們,例如,將成爲一個文字製表符。請參閱我很久以前寫過的關於shell的FAQ文章 - http://cfajohnson.com/shell/cus-faq-2.html#Q24

我將bookName更改爲oldTitle,順便說一句,只是因爲這似乎更有意義相對於newTitle。無功能差異。

在做任何文本操作來了解串和各種口味的正則表達式(BREs裏面/ ERES/PCREs)之間和部分和完全匹配之間的差異是非常重要的。

  1. 的grep上BREs裏面默認情況下,運行在ERES給出的-E ARG,給出的-P ARG PCREs,並給出了-F ARG字符串。
  2. SED上BREs裏面默認情況下運行,上ERES給出的-E ARG。 sed不支持PCRE。 sed也無法在字符串上操作,並使您的正則表達式像字符串一樣行爲很痛苦,請參見is-it-possible-to-escape-regex-metacharacters-reliably-with-sed
  3. awk默認對ERE和字符串都有效。您只需將ERE與正則表達式運算符和帶字符串運算符的字符串一起使用(請參閱https://www.gnu.org/software/gawk/manual/gawk.html#String-Functions)。

所以,如果在你的情況,你需要在你的文字字面上處理接着所有的角色是一個字符串,而不是一個正則表達式,所以你不應該使用它sed的,如果你想快速找到在一個文件中的字符串很高興與部分匹配,你應該使用grep,但如果你想要做什麼超出是諸如更改文件中的字符串或者做一個精確匹配,那麼你應該用awk。現在

+1

哇徹底,易懂的解釋!欣賞它!感謝您指出我的上述代碼也會錯誤地匹配「Jaws:Henchley」。我甚至沒有想到這一點。我從你的評論和回答中學到了很多東西。我花了一個小時仔細查看你的代碼,並最終了解你如何去做。我不能夠感謝你! – JamesPoppycock

+0

不客氣。你可能想考慮使用':'以外的字符作爲你的標題/作者分隔符,儘管':'通常出現在書名中。如果我是你,我會使用製表符作爲分隔符,並在插入數據庫之前將標題或作者名稱中的所有空格轉換爲單個空白字符,從而確保每行中唯一的選項卡是分隔符。我沒有理由認爲製表符應出現在書名或作者姓名中。這將使你想對數據進行的任何進一步操作變得更簡單。 –

+1

這真是太棒了! – JamesPoppycock

0

確定傢伙,我才意識到我啞然爲****

我在外面撕裂我的頭髮了整整一天,所有我所要做的就是要做到這一點。

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

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

Omg我覺得自己要殺了自己。

+0

不,那是錯誤的做法,將與錯誤的匹配失敗(查找「大白鯊:Henchley」當你有「牙醫指南下頜:Henchley McBoring」在目錄中),當新標題包含反向引用(試圖取代任何標題與「戰爭與和平」)或任何BRE元字符出現在bookName或authorNament和其他情況。用於處理文本的UNIX工具是awk。當你發現自己到達shell + grep + sed組合時,停下來拿起awk書(Arnold Robbins的第四版Effective Awk編程),找出正確的方法來做到這一點。 –

1

爲了讓你開始。創建文件

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 

用法:

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 
+0

這是非常過分複雜手頭的任務,並在舊標題包含'它將會失敗:',放在上下文時,當新標題包含「&」,並在部分匹配的情況下在OPS shell腳本,等等。每當你發現自己試圖逃避一個變量中的所有正則表達式元字符以使你的代碼像一個字符串一樣行爲,而不是停止並思考它,然後使用字符串操作而不是正則表達式操作來避免所有的複雜性。 –