[英]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.