我正在创建一个bash脚本,它只是使用grep来查看一堆特定字符串的日志。
但有些事情发生了。
为了测试所有日志文件,文件名为test 1.log,test2.log,test 3.log等。
使用grep命令时:
grep -oHnR TEST Logs/test*
输出包含文件夹中所有文件的所有实例。
但是当使用命令但包含在下面的bash脚本中时:
#!/bin/bash
#start
grep -oHnR $1 $2
#end
输出仅显示1个文件中的实例。
运行脚本时,我使用以下命令:
bash test.bash TEST Logs/test*
以下是预期输出的示例(仅使用grep时会发生什么):
Logs/test2.log:8:TEST
Logs/test2.log:20:TEST
Logs/test2.log:41:TEST
Logs/test.log:2:TEST
Logs/test.log:18:TEST
以下是使用bash脚本时收到的输出示例:
Logs/test2.log:8:TEST
Logs/test2.log:20:TEST
Logs/test2.log:41:TEST
有人可以向我解释为什么会这样吗?
当你拨打电话时
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
这将正确传递所有参数,并且还会处理文件名中的空格之类的任务(您的版本会遇到这些问题)。
要了解发生了什么,您可以使用更简单的脚本:
#!/bin/bash
echo $1
echo $2
这就像你要求的那样输出前两个参数。
您想使用第一个参数,然后将所有其余参数用作输入文件。所以像这样使用shift
:
#!/bin/bash
search=$1
shift
echo "$1"
echo "$@"
另请注意使用双引号。
在您的情况下,因为您希望搜索字符串和文件名以相同的顺序传递给grep
,您甚至不需要shift
:
#!/bin/bash
grep -oHnR -e "$@"
(如果搜索字符串以-e
开头,我添加了-
)
当你打电话给剧本时,没有引用的*
正受到globbing的影响。
使用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 args(因为每个文件扩展将成为一个arg) - 在脚本中添加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目录中的每个文件,其名称以test
开头。
或者,更改脚本以在命令行上允许多个文件:
#!/bin/bash
hold=$1
shift
grep -oHnR $hold $@