簡體   English   中英

循環shell腳本參數並將帶引號的參數傳遞給函數

[英]Looping over shell script arguments and passing quoted arguments to function

我下面有一個腳本,該腳本提供bash腳本的目錄,然后解析命令的標志,以從源文件運行特定功能。

scripts目錄中提供此功能:

function reggiEcho () {
  echo $1
}

這是電流輸出的一些例子

$ reggi --echo hello
hello
$ reggi --echo hello world
hello
$ reggi --echo "hello world"
hello
$ reggi --echo "hello" --echo "world"
hello
world

如您所見,引用的參數不被接受,因為它們應該是“ hello world”,應該正確地回顯。

這是腳本,問題在while循環內。

如何解析這些標志,並保持將帶引號的參數傳遞給函數?

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
STR="$(find $DIR/scripts -type f -name '*.sh' -print)"
ARR=( $STR )
TUSAGE="\n"

for f in "${ARR[@]}"; do
    if [ -f $f ]
    then
        . $f --source-only

        if [ -z "$USAGE" ]
        then
            :
        else
            TUSAGE="$TUSAGE \t$USAGE\n"
        fi

        USAGE=""
    else
        echo "$f not found"
    fi
done 

TUSAGE="$TUSAGE \t--help (shows this help output)\n"

function usage() {
  echo "Usage: --function <args> [--function <args>]"
  echo $TUSAGE
  exit 1
}

HELP=false

cmd=()
while [ $# -gt 0 ]; do                         # loop until no args left

    if [[ $1 = '--help' ]] || [[ $1 = '-h' ]] || [[ $1 = '--h' ]] || [[ $1 = '-help' ]]; then
        HELP=true
    fi

    if [[ $1 = --* ]] || [[ $1 = -* ]]; then                    # arg starts with --
        if [[ ${#cmd[@]} -gt 0 ]]; then
            "${cmd[@]}"
        fi
        top=`echo $1 | tr -d -`                              # remove all flags
        top=`echo ${top:0:1} | tr  '[a-z]' '[A-Z]'`${top:1}  # make sure first letter is uppercase
        top=reggi$top                                         # prepend reggi
        cmd=( "$top" )                                       # start new array
    else
        echo $1
        cmd+=( "$1" )
    fi
    shift
done

if [[ "$HELP" = true ]]; then
    usage
elif [[ ${#cmd[@]} -gt 0 ]]; then
    ${cmd[@]}
else
    usage
fi

在此腳本中的很多地方,變量引用周圍都沒有雙引號。 這意味着變量的值將受到單詞分散和通配符擴展的影響,這可能會產生各種奇怪的影響。

您看到的特定問題是由於倒數第四行${cmd[@]}上沒有引用的變量引用引起的。 使用cmd=( echo "hello world" ) ,單詞拆分使它等效於echo hello world而不是echo "hello world"

解決這一問題將解決您當前的問題,但是還有許多其他未引用的變量引用可能在以后引起其他問題。 我建議修復所有這些問題。 賽勒斯(Cyrus)對shellcheck.net的推薦擅長於指出它們,並且還將指出我在這里不會涉及的其他一些問題。 它不會提及的一件事是,您應該避免使用全大寫字母的變量名( DIRTUSAGE等)-一堆具有特殊含義的全大寫字母變量,很容易意外地重用其中的一個並繞開產生怪異的效果。 小寫和大小寫混合的變量更安全。

我還建議不要在字符串中使用\\t\\n ,而應該依靠echo將它們分別轉換為制表符和換行符。 某些版本的echo會自動執行此操作,某些版本需要-e選項來告訴他們執行此操作,某些版本會在其輸出中打印“ -e” ...這是一團糟。 在bash中,您可以使用$'...'直接轉換這些轉義序列,例如:

tusage="$tusage"$' \t--help (shows this help output)\n'    # Note mixed quoting modes
echo "$tusage"    # Note that double-quoting is *required* for this to work right

您還應該修復文件列表,以使其不依賴於未引用(請參閱chepner的評論)。 如果您不需要掃描$ DIR / scripts的子目錄,則可以使用一個簡單的通配符(請注意小寫的vars,並且該var被雙引號括起來,但是通配符不是):

arr=( "$dir/scripts"/*.sh )

如果您需要查看子目錄,則更為復雜。 如果您具有bash v4,則可以使用globstar通配符,如下所示:

shopt -s globstar
arr=( "$dir/scripts"/**/*.sh )

如果您的腳本可能必須在bash v3下運行,請參見BashFAQ#20:“如何查找並安全地處理包含換行符,空格或兩者的文件名?” ,或僅使用此代碼:

while IFS= read -r -d '' f <&3; do
    if [ -f $f ]
        # ... etc
done 3< <(find "$dir/scripts" -type f -name '*.sh' -print0)

(這是我最喜歡的遍歷find的匹配項的“只管用”的成語。盡管它確實需要bash,但不需要某些通用的POSIX shell。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM