我需要一个简单的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.
答案 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
}