Unix bash切割和grep

时间:2013-01-09 14:06:29

标签: bash unix grep cut

我有一个名为db.txt的文本文件。 文件中的一些样本行如下:

  

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

     

哈利波特与密室:J.K。罗琳:21.95:150:300

     

指环王,戒指团契:J.R.R。托尔金:32.00:500:500

     

权力的游戏:George R.R. Martin: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

有没有办法从cut剪切特定字段然后将整行传入while循环?

例如,如果我进入“哈利波特”, 它应该显示:

  

哈利波特与魔法石,J.K。罗琳,21.95美元,100,200

     

哈利波特与密室,J.K。罗琳,21.95美元,150美元,300美元

6 个答案:

答案 0 :(得分: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

答案 1 :(得分:4)

grepcut的下一步是awk。除非你必须使用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 :(得分: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命令中的测试执行简单的全局匹配但不区分大小写,因此如果用户输入“potter”,它将匹配。

或者,使用sed更改分隔符:

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

表示删除与TITLE不匹配的所有行,然后转换分隔符。

答案 3 :(得分: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将分隔符更改为:以允许访问字段

答案 4 :(得分: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答案是最好的。

答案 5 :(得分: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