[英]Using a glob expression passed as a bash script argument
为什么当myscript
var=$1
与使用var=foo*
硬编码的./myscript
调用时,为什么不调用./myscript foo*
?
我在编写的bash脚本中遇到了一个奇怪的问题。 我敢肯定有一个简单的解释,但我无法弄清楚。
我正在尝试传递命令行参数,以在脚本中将其分配为变量。
我希望脚本允许2个命令行参数,如下所示:
$ bash my_bash_script.bash args1 args2
在我的脚本中,我分配了如下变量:
ARGS1=$1
ARGS2=$2
Args 1是要添加到输出文件的字符串描述符。
Args 2是一组目录:“ dir1,dir2,dir3”,我将其作为dir*
传递
当我在脚本中将dir*
分配给ARGS2时,它可以正常工作,但是当我将dir*
作为第二个命令行参数传递时,它仅在dir*
的通配符扩展中包括dir1
。
我认为这与外壳处理通配符(即使以args形式传递)的方式有关,但是我不太了解。
任何帮助,将不胜感激。
我有一组目录:
dir_1_y_map, dir_1_x_map, dir_2_y_map, dir_2_x_map,
... dir_10_y_map, dir_10_x_map...
在这些目录中,我尝试通过*.status
访问扩展名为".status"
的文件,并通过*report.txt
访问扩展名为".report.txt"
。
我想将dir_*_map
作为第二个参数传递给脚本,并将其存储在变量ARGS2中,然后使用它在每个目录中搜索".status"
和".report"
文件。
问题是从命令行传递dir_*_map
不会给出目录列表,而只是给出列表中的第一项。 如果我在脚本中分配变量ARGS2=dir_*_map
,则它将按我的ARGS2=dir_*_map
工作。
事实证明,在引号中传递第二个参数允许通配符扩展适用于"dir_*_map"
#!/usr/bin/env bash
ARGS1=$1
ARGS2=$2
touch $ARGS1".extension"
for i in /$ARGS2/*.status
do
grep -e "string" $i >> $ARGS1".extension"
done
这是脚本的示例调用:
sh ~/path/to/script descriptor "dir_*_map"
我不完全理解何时/为什么必须在引号中传递某些参数,但我认为这与for循环中的通配符扩展有关。
就像var=foo*
那样,赋值不会扩展全局变量-也就是说,当您运行var=foo*
,文字字符串foo*
被放入变量foo
,而不是与foo*
匹配的文件列表中。
相比之下,在命令行上不加引号的foo*
扩展了glob,将其替换为单个名称列表,每个名称均作为单独的参数传递 。
因此,运行./yourscript foo*
不会将foo*
作为$1
传递,除非不存在与该glob表达式匹配的文件。 相反,它变成类似于./yourscript foo01 foo02 foo03
东西,每个参数都位于命令行的不同位置。
运行./yourscript "foo*"
作为替代方法的原因是脚本内部未引用的扩展允许在以后扩展glob。 但是,这是一种不好的做法:全局扩展与字符串拆分同时发生(这意味着依靠此行为将使您无法传递包含在IFS
找到的字符(通常为空格)的文件名),并且还意味着在以下情况下不能传递文字文件名:它们也可以解释为glob(如果您有一个名为[1]
的文件和一个名为1
的文件,则传递[1]
将始终替换为1
)。
建立这种惯用的方法是shift
掉的第一个参数,然后叠代以后的,就像这样:
#!/bin/bash
out_base=$1; shift
shopt -s nullglob # avoid generating an error if a directory has no .status
for dir; do # iterate over directories passed in $2, $3, etc
for file in "$dir"/*.status; do # iterate over files ending in .status within those
grep -e "string" "$file" # match a single file
done
done >"${out_base}.extension"
如果单个目录中有多个.status
文件,则可以使用find
调用具有尽可能多参数的grep
,而不是逐个文件地单独调用grep
,从而使所有这一切更有效。
#!/bin/bash
out_base=$1; shift
find "$@" -maxdepth 1 -type f -name '*.status' \
-exec grep -h -- /dev/null '{}' + \
>"${out_base}.extension"
上面的两个脚本都希望传递的glob在调用shell上不被引用。 因此,用法的形式为:
# being unquoted, this expands the glob into a series of separate arguments
your_script descriptor dir_*_map
这比将glob传递到脚本(然后将其扩展以检索要使用的实际文件)要好得多。 它可以正确处理包含空格的文件名(其他做法则不这样做)以及名称本身就是glob表达式的文件。
其他注意事项:
"$dir"/*.status
。status的情况),请在遍历表达式开始之前结束引号。 for dir; do
for dir; do
等同for dir in "$@"; do
for dir in "$@"; do
,它遍历参数。 不要犯for dir in $*; do
中使用for dir in $*; do
的错误for dir in $*; do
for dir in $*; do
或for dir in $@; do
for dir in $@; do
吧! 后面的这些调用将列表的每个元素与IFS
的第一个字符(默认情况下,该字符按顺序包含空格,制表符和换行符)组合在一起,然后在其中找到的所有IFS
字符上分割结果字符串,然后展开每个字符结果列表的组成部分作为一个整体。 /dev/null
作为参数传递给grep
是一种安全措施:确保单参数和多参数情况之间没有不同的行为(例如, grep
默认仅在传递时在输出中打印文件名)多个参数),并确保您不会让grep
挂起尝试从stdin读取(如果它根本没有传递任何其他文件名)(在这里find
,但xargs
可以)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.