2016-08-13 6 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 LAMBMary is not a lambに変更する場合は、新しいタイトルを入力し、cutText.txtは元のタイトルをMary is not a lambに置き換えます。

$newTitleのユーザーが「Mary is little lamb」と入力した場合、このコードはケースを考慮に入れているため機能しません。 「MARY IS LITTLE LAMB」のユーザータイプのみが動作します。私はBEGIN{IGNORECASE=1}がgawk-sepcificであることに気付きました。したがって、awkでは使用できません。

このようにスクリプトを作成すると、ユーザー入力の大文字小文字を無視できますか。ありがとうございました!私はちょうど私が、私は一日のために私の髪を引き裂くたし、私がしなければならなかったすべては、これを行うことでした

**** AS DUMB AM REALIZED

+1

おそらく、ユーザーが$ bookName_の "Mary is little lamb"と入力すると、_Aの問題が発生する可能性があります。とにかく、これは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 

&)または)\(バックスラッシュ場合は、任意のフィールドや日付にあなたの他のスクリプトが上失敗する他のいずれかの状況で表示されます。 ARGV []は、私が-v var=valまたはvar=valその後、バックスラッシュが解釈される引数リストの中など\tを使用して実装されている場合ので、例えば、リテラルのタブ文字になるから

$ 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の変数を移入しています。私が長年前に書いたシェルFAQの記事 - http://cfajohnson.com/shell/cus-faq-2.html#Q24を参照してください。

newTitleとの関係でより意味のあるように思われるので、bookNameoldTitleに変更しました。機能的な違いはありません。

テキスト操作を行うときは、文字列とさまざまな正規表現フレーバー(BRE/ERE/PCRE)の違い、部分一致と完全一致の違いを理解することが非常に重要です。

  1. grepがのEREに-P引数を与えPCREsに、-E引数を与えられ、そして-F引数を指定した文字列に対して、デフォルトではBREでは上で動作します。
  2. sedはデフォルトでBREに作用し、-E argを指定したEREで動作します。 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つの空白文字に変換して、各行の唯一のタブがセパレータであることを確認します。タブの文字が書籍のタイトルや著者名に表示されるべきだと考える理由はありません。これにより、データに対する操作をより簡単に行うことができます。 –

+1

それは素晴らしいです! – JamesPoppycock

0

OK GUYS。

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

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

私は自分自身を殺したように感じます。

+0

いいえ、それは間違ったアプローチであり、誤ったマッチで失敗するでしょう(あなたのカタログの "歯科医師ガイド:Henchley McBoring"があるときは "Jaws:Henchley"を参照してください)、新しいタイトルに後方参照が含まれている"War&Peace"のタイトル)、またはBREメタキャラクターがbookNameまたはauthorNamentなどの状況で表示されます。テキストを操作するためのUNIXツールはawkです。あなたがシェル+ grep + sedの組み合わせに達するのを見つけたら、代わりにawkの本(Effective Awk Programming、第4版、Arnold Robbins)を手に入れて、正しい方法を見つけてください。 –

1

ご利用になるには

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

予想される出力:

ファイルにcutText.txt

Hairy Potter:Rihanna 
MARY IS A LITTLE 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 
} 

を作成します。

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

古いタイトルに ':'が含まれていて、新しいタイトルに "&"が含まれていて、OPシェルスクリプトなどのコンテキストに置かれている部分一致の状況では、それは失敗します。変数内のすべてのregexpメタキャラクタからエスケープしようとすると、文字列であるかのようにコードが動作するようになり、そのことを考えるだけで、その複雑さを避けるためにregexp操作の代わりに文字列操作を使用します。 –

関連する問題