2013-01-09 51 views
5

我有一個名爲db.txt的文本文件。 文件中的一些採樣行如下:Unix bash切割和grep

哈利波特與魔法石:J.K。羅琳:21.95:100:200

哈利波特與密室:J.K。羅琳:21.95:150:300

魔戒,戒指之友:J.R.R。托爾金: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循環?

例如,如果我進入了「哈利·波特」, 它應該顯示:

哈利·波特與魔法石,J.K.羅琳,$ 21.95,100,200

哈利波特與密室,J.K.羅琳,$ 21.95,150,300

+0

shell中行上的循環是異常的。所有的命令(grep,sed,cut等等)都使用線上的循環。 – Zulu

回答

5

如果你確定使用bash的正則表達式匹配(或者可以使用shell模式匹配,而不是),你可以做到這一點沒有cut,沒有grep

想法是逐行讀取文件,然後將行分割成數組。 一旦你有了,做比較和輸出你想要的。

下面是該技術的演示:

#! /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 
+0

我還想補充一件事,你如何檢查不區分大小寫的情況,以及如何在後面打印出數字?我試過%d,但數字最終爲零。 –

+0

取消註釋文章中的'shopt'行以獲得不區分大小寫的匹配。試試'$%。02f'爲美元數量,'%d'爲整數值可以很好地處理您提供的數據。 – Mat

+0

grep「哈利波特」db.txt應該這樣做! –

4

下一步了從grepcutawk。除非你必須做到這一點使用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 
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改變分隔符爲:允許領域

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 

這意味着刪除不要在冠軍爭奪戰中的所有行,然後變換的分隔符。

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 
2

我知道你沒有指定,但awk可能是最好的工具用於此任務。它將cut,sed和grep結合到一個方便易用的工具中。好了,方便的工具...

要了解awk,你要明白幾件事情:

  • AWK是一種編程語言。它內置了邏輯和變量。
  • Awk假設讀取循環讀取每一行。
  • awk程序必須用大括號包圍。
  • 不僅大括號,而且awk解析變量以美元符號開頭。因此,您需要將您的Awk程序包圍在單引號內,以使shell不在其中。
  • Awk根據字段分隔符自動分析每行。默認字段分隔符是一個空格,但您可以通過參數-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 

當然,整件事可能是一個太行:

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

只是想強調awk和怎樣的編程能力它可以用來做這種類型的解析。

如果您的問題是如何輸入此信息並將其放入環境變量中,則Glenn Jackman's答案是最好的。