Перебирая аргументы сценария оболочки и передавая аргументы в кавычки в функцию - PullRequest
0 голосов
/ 31 мая 2018

У меня есть скрипт ниже, который создает каталог скриптов bash, а затем анализирует флаги команды для запуска определенной функции из исходных файлов.

С учетом этой функции в scripts dir:

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

1 Ответ

0 голосов
/ 01 июня 2018

В этом скрипте есть много мест, где у вас есть ссылки на переменные без двойных кавычек.Это означает, что значения переменных будут подвержены разбивке слов и расширению подстановочного знака, что может иметь различные странные эффекты.

Конкретная проблема, с которой вы сталкиваетесь, связана с ссылкой на переменную без кавычек в четвертой от последнейлиния, ${cmd[@]}.При cmd=( echo "hello world" ) разбиение слов делает это эквивалентным echo hello world, а не echo "hello world".

Исправление одной строки решит вашу текущую проблему, но есть ряд других ссылок на переменные без кавычек, которые могут вызвать другиепроблемы позже.Я рекомендую исправить всех из них.Рекомендация Сайруса о shellcheck.net хороша для их указания, а также отметит некоторые другие вопросы, которые я не буду здесь освещать.Одна вещь, о которой не следует упоминать, это то, что вам следует избегать имен переменных all-caps (DIR, TUSAGE и т. Д.) - есть куча переменных all-caps со специальным значением, и их легко случайно использовать повторноиз них и заводить со странными эффектами.Переменные в нижнем и нижнем регистре безопаснее.

Я также рекомендую не использовать \t и \n в строках и рассчитывать на echo для перевода их во вкладки и переводы строки соответственно.Некоторые версии echo делают это автоматически, некоторые требуют опции -e, чтобы сказать им сделать это, некоторые выводят «-e» как часть их вывода ... это беспорядок.В bash вы можете использовать $'...' для прямого перевода этих escape-последовательностей, например:

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

Вы также должны исправить листинг файла, чтобы он не зависел от того, чтобы быть без кавычек (см. Комментарий Чепнера).Если вам не нужно сканировать подкаталоги $ DIR / scripts, вы можете сделать это с помощью простого подстановочного знака (обратите внимание на строчные буквы и переменную в двойных кавычках, но подстановочный знак нет):

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.)

...