5

db.txtというテキストファイルがあります。ファイルの一部のサンプル行は次のようになります。

ハリーポッターと賢者の石:JKローリング:21.95:100:200

ハリーポッターと秘密の部屋:JKローリング:21.95:150:300

ロードオブザリング、リングのフェローシップ:JRRトールキン:32.00:500:500

七王国の玉座:ジョージRRマーティン:44.50:300:250

次に、私のスクリプトには、次の行があります。

echo "Enter title:"
read TITLE

cut -d ":" -f 1 db.txt | grep -iw "$TITLE" | while read LINE
do
    STRING="`echo $LINE | cut -d ":" -f 1`,"
    STRING="$STRING `echo $LINE | cut -d ":" -f 2`, "
    STRING=" \$$STRING`echo $LINE | cut -d ":" -f 3`,"
    STRING=" $STRING`echo $LINE | cut -d ":" -f 4`,"
    STRING=" $STRING`echo $LINE | cut -d ":" -f 5`"
done

カットから特定のフィールドをgrepしてから、フルラインをwhileループに渡す方法はありますか?

たとえば、「ハリーポッター」と入力すると、次のように表示されます。

ハリーポッターと賢者の石、JKローリング、21.95ドル、100、200

ハリーポッターと秘密の部屋、JKローリング、21.95ドル、150、300ドル

4

6 に答える 6

5

これは、なしで、 bashの正規表現マッチングに問題がない場合はなくても実行できます(または、代わりにシェルパターンマッチングを使用できます)cutgrep

アイデアは、ファイルを1行ずつ読み取り、その行を配列に分割することです。それができたら、必要な比較と出力を行います。

テクニックのデモは次のとおりです。

#! /bin/bash
echo "Title:"
read title

# shopt -s nocasematch           # if you want case-insensitive matching

while read line ; do             # this read takes data from input.txt, see
                                 # end of loop
        IFS=: read -a parts <<< "$line"  # this splits the line on ":" into
                                         # an array called parts

        if [[ ${parts[0]} =~ $title ]] ; then  # regex matching
                printf "%s -- %s\n" "${parts[1]}" "${parts[2]}"
        fi
done < input.txt
于 2013-01-09T14:22:36.517 に答える
4

grepからの次のステップcutawkです。bash(これは宿題ですか?)を使用してこれを行う必要がない限り、awk物事はかなり簡単になります:

awk -F: '/harry potter/ { sub(/^/,"$",$(NF-2)); print }' IGNORECASE=1 OFS=", " db.txt

テスト入力:

Harry Potter and the Sorcerer's Stone:J.K. Rowling:21.95:100:200
Harry Potter and the Chamber of Secrets:J.K. Rowling:21.95:150:300
Lord of the Rings, The Fellowship of the Ring:J.R.R. Tolkien:32.00:500:500
A Game of Thrones:George R.R. Martin:44.50:300:250

テスト出力:

Harry Potter and the Sorcerer's Stone, J.K. Rowling, $21.95, 100, 200
Harry Potter and the Chamber of Secrets, J.K. Rowling, $21.95, 150, 300
于 2013-01-09T14:26:56.133 に答える
3
read -p "Enter title: " TITLE
while IFS=: read title author price x y; do
    if [[ ${title,,} == *${TITLE,,}* ]]; then
        printf "%s, %s, $%s, %s, %s\n" "$title" "$author" "$price" "$x" "$y"
    fi
done < db.txt

ifコマンドのテストは、単純なglob-matchを実行しますが、大文字と小文字を区別しないため、ユーザーが「potter」と入力すると一致します。

または、sedを使用して区切り文字を変更します。

read -p "Enter title: " TITLE
sed '/'"$TITLE"'/I!d; s/:/, /g' db.txt

これは、タイトルと一致しないすべての行を削除してから、区切り文字を変換することを意味します。

于 2013-01-09T14:31:04.637 に答える
2

あなたがそれを指定しなかったことは知っていますが、awkおそらくこのタスクに使用するのに最適なツールです。これは、cut、sed、およびgrepを1つの便利で使いやすいツールに組み合わせたものです。さて、便利なツール...

を理解するawkには、いくつかのことを理解する必要があります。

  • Awkはプログラミング言語です。ロジックと変数が組み込まれています。
  • Awkは、すべての行を読み取る読み取りループを想定しています。
  • Awkプログラムは、中括弧で囲む必要があります。
  • 中括弧だけでなく、Awkの解析変数はドル記号で始まります。したがって、シェルを除外するために、Awkプログラムを一重引用符で囲む必要があります。
  • Awkは、フィールド区切り文字に基づいて各行を自動的に解析します。デフォルトのフィールド区切り文字はwhileスペースですが、-fパラメーターを使用して変更できます。
  • フィールドは特別な変数を取得します。最初のフィールドは、、$1次のフィールドは$2、などです。行全体は$0です。

これがあなたのAwkステートメントです:

awk -F: '{
    title =  $1
    author = $2
    price  = $3
    pages_read_until_i_got_bored=$4
    pages = $5
    print "I read " pages_read_until_i_gob_bored "pages out of " $pages " pages of " $title " by " $author "."
}' $file

もちろん、すべてが1行になることもあります。

 awk -F: '{ print "I read " $4 " pages " out of " $5 " of " $1 " by " $2 "." }' $file

Awkのプ​​ログラム可能性と、このタイプの解析を行うためにAwkをどのように使用できるかを強調したかっただけです。

この情報を入力して環境変数に入れる方法が質問の場合は、GlennJackmanの答えが最適です。

于 2013-01-09T14:59:01.867 に答える
2

これを行う最も簡単な方法は、grepの結果を調べることです。

#!/bin/bash

read -p "Enter title: " TITLE

FILENAME="db.txt"
IFS=$'\n'
for LINE in `grep -iw  "Harry Potter" "$FILENAME"`; do
    echo $LINE | awk 'BEGIN { FS = ":" } ; { print $1, $2, $3, $4, $5 }'
done

IFSの変更により、区切り文字がスペースではなく改行に変更され、awkコマンドのFSによって区切り文字が:に変更されて、フィールドにアクセスできるようになります。

于 2013-01-09T14:29:09.790 に答える
1

あなたが使用できる場合sed、これは解決策になります

  read -p "Enter title: " TITLE
  sed -n -e 's/^\([^:]\+:\)\{2\}/\0$/' -e 's/:/, /g' -e "/^$TITLE/Ip" db.txt

それが何をするか簡単な説明

 -n tells sed not to print any lines
 -e 's/^\([^:]\+:\)\{2\}/\0$/' matches for the 2nd : and adds a $ after it
 -e 's/:/, /g' replaces all : with , and a following whitespace
 -e "/^$TITLE/Ip" tells sed to print all lines which start with $TITLE (that's the p) and I tells sed to match case-insensitive
于 2013-01-09T14:31:38.507 に答える