bash脚本中的grep输出不同

bash脚本中的grep输出不同,bash,command-line-arguments,Bash,Command Line Arguments,我正在创建一个bash脚本,它将简单地使用grep在一堆日志中查找某个字符串 不过发生了一些有趣的事情 为了测试所有日志文件,文件名为test1.log、test2.log、test3.log等 使用grep命令时: grep -oHnR TEST Logs/test* 输出按预期包含文件夹中所有文件的所有实例 但是当使用下面的bash脚本中包含的命令时: #!/bin/bash #start grep -oHnR $1 $2 #end 输出仅显示1个文件中的实例 运行脚本时,我使用以下

我正在创建一个bash脚本,它将简单地使用grep在一堆日志中查找某个字符串

不过发生了一些有趣的事情

为了测试所有日志文件,文件名为test1.log、test2.log、test3.log等

使用grep命令时:

grep -oHnR TEST Logs/test*
输出按预期包含文件夹中所有文件的所有实例

但是当使用下面的bash脚本中包含的命令时:

#!/bin/bash
#start

grep -oHnR $1 $2

#end
输出仅显示1个文件中的实例

运行脚本时,我使用以下命令:

bash test.bash TEST Logs/test*
下面是一个预期输出的示例(仅使用grep时发生的情况):

下面是使用bash脚本时收到的输出示例:

Logs/test2.log:8:TEST    
Logs/test2.log:20:TEST    
Logs/test2.log:41:TEST

有人能给我解释一下为什么会发生这种情况吗?

要了解发生了什么,可以使用一个更简单的脚本:

#!/bin/bash
echo $1
echo $2
这将输出前两个参数,如您所要求的

您希望使用第一个参数,然后使用所有其他参数作为输入文件。所以像这样使用
shift

#!/bin/bash
search=$1
shift

echo "$1"
echo "$@"
还要注意双引号的使用

在您的情况下,因为您希望搜索字符串和文件名以相同的顺序传递给
grep
,所以您甚至不需要
shift

#!/bin/bash

grep -oHnR -e "$@"

(我添加了
-e
,以防搜索字符串以
-
开头)

要了解发生了什么,可以使用更简单的脚本:

#!/bin/bash
echo $1
echo $2
这将输出前两个参数,如您所要求的

您希望使用第一个参数,然后使用所有其他参数作为输入文件。所以像这样使用
shift

#!/bin/bash
search=$1
shift

echo "$1"
echo "$@"
还要注意双引号的使用

在您的情况下,因为您希望搜索字符串和文件名以相同的顺序传递给
grep
,所以您甚至不需要
shift

#!/bin/bash

grep -oHnR -e "$@"
(我添加了
-e
,以防搜索字符串以
-
开头)

呼叫线路时

bash test.bash TEST Logs/test*
这将由shell转换为

bash test.bash TEST Logs/test1.log Logs/test2.log Logs/test3.log Logs/test4.log
(如果您有四个日志文件)

命令行参数
TEST
Logs/test1.log
Logs/test2.log
等将被命名为
$1
$2
$3
等<代码>$1将是
TEST
$2
将是
Logs/test1.log

当您仅使用
$2
时,您只需忽略其余参数,只需使用一个日志文件

正确的版本如下:

#!/bin/bash
#start

grep -oHnR "$@"

#end
这将正确地传递所有参数,还可以处理文件名中的空格之类的污点(您的版本可能会遇到这些问题)。

当您调用该行时

bash test.bash TEST Logs/test*
这将由shell转换为

bash test.bash TEST Logs/test1.log Logs/test2.log Logs/test3.log Logs/test4.log
(如果您有四个日志文件)

命令行参数
TEST
Logs/test1.log
Logs/test2.log
等将被命名为
$1
$2
$3
等<代码>$1将是
TEST
$2
将是
Logs/test1.log

当您仅使用
$2
时,您只需忽略其余参数,只需使用一个日志文件

正确的版本如下:

#!/bin/bash
#start

grep -oHnR "$@"

#end

这将正确地传递所有参数,并处理文件名中的空格等不干净的内容(您的版本可能会遇到这些问题)。

调用脚本时,不带引号的
*
会受到影响

使用
set-x
输出脚本中正在运行的内容可以使这一点更加清楚

$ ./greptest.sh TEST test*
++ grep -oHnR TEST test1.log
$ ./greptest.sh TEST "test*"
++ grep -oHnR TEST test1.log test2.log test3.log
在第一种情况下,bash将*扩展到文件名列表中,而在第二种情况下,它将被传递给grep。在第一种情况下,您实际上有>2个参数(因为每个文件名扩展后都会变成一个参数)-向脚本添加
echo$#
也会显示这一点:

$ ./greptest.sh TEST test*
++ grep -oHnR TEST test1.log
++ echo 4
4
$ ./greptest.sh TEST "test*"
++ grep -oHnR TEST test1.log test2.log test3.log
++ echo 2
2

调用脚本时,未加引号的
*
会受到影响

使用
set-x
输出脚本中正在运行的内容可以使这一点更加清楚

$ ./greptest.sh TEST test*
++ grep -oHnR TEST test1.log
$ ./greptest.sh TEST "test*"
++ grep -oHnR TEST test1.log test2.log test3.log
在第一种情况下,bash将*扩展到文件名列表中,而在第二种情况下,它将被传递给grep。在第一种情况下,您实际上有>2个参数(因为每个文件名扩展后都会变成一个参数)-向脚本添加
echo$#
也会显示这一点:

$ ./greptest.sh TEST test*
++ grep -oHnR TEST test1.log
++ echo 4
4
$ ./greptest.sh TEST "test*"
++ grep -oHnR TEST test1.log test2.log test3.log
++ echo 2
2

您可能希望在bash调用时转义通配符:

bash test.bash TEST Logs/test\*
这样,它将以
*
的形式传递给grep,否则shell将把它扩展到Logs dir中名称以
test
开头的每个文件

或者,更改脚本以允许命令行上有多个文件:

#!/bin/bash
hold=$1
shift
grep -oHnR $hold $@

您可能希望在bash调用时转义通配符:

bash test.bash TEST Logs/test\*
这样,它将以
*
的形式传递给grep,否则shell将把它扩展到Logs dir中名称以
test
开头的每个文件

或者,更改脚本以允许命令行上有多个文件:

#!/bin/bash
hold=$1
shift
grep -oHnR $hold $@

⁺用于提及
-e
:)⁺因为提到
-e
:)哇!我对参数在bash中如何工作的想法是完全错误的。这很有道理。谢谢你的回复。哇!我对参数在bash中如何工作的想法是完全错误的。这很有道理。谢谢你的回复。