shell脚本在文件列表中查找单词,所有这些单词都作为参数给出

时间:2015-03-28 21:02:06

标签: linux bash shell

我需要一个简单的shell程序,它必须做这样的事情:

script.sh word_to_find file1 file2 file3 .... fileN

将显示

word_to_find 3 - if word_to_find appears in 3 files

word_to_find 5 - if word_to_find appears in 5 files 

这是我尝试过的

#!/bin/bash

count=0
for i in $@; do
  if [ grep '$1' $i ];then
     ((count++))
  fi
done
echo "$1 $count"

但是会显示此消息:

syntax error: "then" unexpected (expecting "done").

在此之前错误是

[: grep: unexpected operator.

4 个答案:

答案 0 :(得分:2)

您展示的代码是:

#!/bin/bash

count=0
for i in $@; do
  if [ grep '$1' $i ];then
     ((count++))
  fi
done
echo "$1 $count"

当我运行它时,我收到错误:

script.sh: line 5: [: $1: binary operator expected

这是合理的,但它与问题中报告的任何错误都不一样。代码中存在多个问题。

for i in $@; do应为for i in "$@"; do。始终使用"$@",以便保留参数中的任何空格。如果您的文件名都没有包含空格或制表符,那么它并不重要,但这是一个很好的习惯。 (有关详细信息,请参阅How to iterate over arguments in bash script。)

if操作运行[(又名test)命令,该命令实际上是内置的shell以及/bin或{{1}中的二进制文件}}。在/usr/bin周围使用单引号意味着该值未展开,并且该命令将其参数视为:

'$1'

其中第一个是命令名,或者是C中的[ grep $1 current-file-name ] ,或者是shell中的argv[0]。我得到的错误是因为$0命令需要test=等运算符出现-lt的位置(也就是说,它需要一个二元运算符,而不是$1,因此消息)。

您实际上想要测试$1是否在grep中找到了每个文件中的单词($1后面列出的名称)。您可能希望像这样编码,然后:

$1

我们可以就#!/bin/bash word="$1" shift count=0 for file in "$@" do if grep -l "$word" "$file" >/dev/null 2>&1 then ((count++)) fi done echo "$word $count" 使用的选项和I / O重定向进行协商。 POSIX grep 选项grep和/或-q选项提供不同程度的沉默,-s可用于代替-q。如果找到该单词,-l选项只会列出文件名,并在第一次出现时停止扫描。 I / O重定向确保丢弃错误,但测试确保计算成功匹配。


声明的输出不正确

有人声称上面的代码没有产生正确的答案。这是我进行的测试:

-l

这表明对于给定的文件,我的机器上的输出是正确的(Mac OS X 10.10.2; GNU bash,版本3.2.57(1)-release(x86_64-apple-darwin14))。如果等效测试在您的计算机上的工作方式不同,则(a)请标识机器和Bash的版本($ echo "This country is young" > young.iii $ echo "This country is little" > little.iii $ echo "This fruit is fresh" > fresh.txt $ bash findit.sh country young.iii fresh.txt little.iii country 2 $ bash -x findit.sh country young.iii fresh.txt little.iii + '[' -f /etc/bashrc ']' + . /etc/bashrc ++ '[' -z '' ']' ++ return + alias 'r=fc -e -' + word=country + shift + count=0 + for file in '"$@"' + grep -l country young.iii + (( count++ )) + for file in '"$@"' + grep -l country fresh.txt + for file in '"$@"' + grep -l country little.iii + (( count++ )) + echo 'country 2' country 2 $ ),并且(b)请使用您从bash --version看到的输出更新问题。您可能需要创建子目录(例如bash -x findit.sh country young.iii fresh.txt little.iii),并在创建文件之前将junk复制到该目录中,等等。

您还可以通过显示以下内容的输出来支持您的案例:

findit.sh

答案 1 :(得分:2)

试试这个:

#!/bin/sh
printf '%s %d\n' "$1" $(grep -hm1 "$@" | wc -l)

注意所有脚本的参数是如何逐字传递给grep的 - 第一个是搜索表达式,其余是文件名。

grep -hm1的输出是一个匹配列表,每个匹配一个文件一个,wc -l计算它们。

我最初使用grep -l发布了这个答案,但这需要文件名永远不会包含换行符,这是一个相当麻烦的限制。

如果不需要正则表达式搜索(例如只搜索文字文本),可能会添加-F选项。

答案 2 :(得分:0)

#!/usr/bin/perl
use strict;
use warnings;
my $wordtofind = shift(@ARGV);
my $regex = qr/\Q$wordtofind/s;
my @file = ();
my $count = 0;
my $filescount = scalar(@ARGV);
for my $file(@ARGV)
{
    if(-e $file)
    {
        eval { open(FH,'<' . $file) or die "can't open file $file "; };
        unless($@)
        {
            for(<FH>)
            {
                if(/$regex/)
                {
                     $count++;
                     last;
                }
            }
            close(FH);
        }
    }
}
print "$wordtofind $count\n";

答案 3 :(得分:0)

您可以使用Awk脚本:

#!/usr/bin/env awk -f

BEGIN {
    n=0
} $0 ~ w {
    n++
} END {
    print w,n
}

然后像这样运行:

./script.awk w=word_to_find file1 file2 file3 ... fileN

或者如果您不想担心在命令行上分配变量(w):

BEGIN {
    n=0
    w=ARGV[1]
    delete ARGV[1]
} $0 ~ w {
    n++
} END {
    print w,n
}