передача опций/аргументов/параметров с пробелами из скрипта в функцию

Вопрос или проблема

У меня есть скрипт, которому я передаю аргументы/опции/параметры через командную строку. Одно из значений содержит пробел, поэтому я заключил его в двойные кавычки. Возможно, будет легче предоставить пример. Простите за использование аргументов/опций/параметров.

$:  ./test1.ksh -n -b -d "Home Videos"

Моя проблема заключается в присвоении переменной значения “Home Videos” и его использовании вместе. В моем примере флаг -d указывает на директорию. Не все директории содержат пробелы, но в моем случае некоторые содержат.

Вот пример кода, который у меня есть, и который не работает так, как ожидалось:

#!/bin/ksh

Function1()
{
echo "Number of Args in Function1: $#"
echo "Function1 Args: $@"
SetArgs $*
}

SetArgs()
{
echo -e "\nNumber of Args in SetArgs: $#"
echo "SetArgs Args: $@"
while [ $# -gt 0 ]
do
  case $1 in
    -[dD])
    shift
    export DirectoryName=$1
    ;;
    -[nN])
    export Var1=No
    shift
    ;;
    -[bB])
    export Var2=Backup
    shift
    ;;
    *)
    shift
    ;;
  esac
done
Function2
}

Function2()
{
echo "Directory Name: ${DirectoryName}"
}

Function1 $*

Когда я запускаю это, я получаю только Home в DirectoryName вместо Home Videos. См. ниже.

 $ ./test1.ksh -n -b -d "Home Videos"
 Number of Args in Function1: 5
 Function1 Args: -n -b -d Home Videos

 Number of Args in SetArgs: 5
 SetArgs Args: -n -b -d Home Videos
 Var1 is set to:  No
 Var2 is set to:  Backup
 Directory Name: Home

Чего я ожидаю и не могу получить:

 $ ./test1.ksh -n -b -d "Home Videos"
 Number of Args in Function1: 4
 Function1 Args: -n -b -d "Home Videos"

 Number of Args in SetArgs: 4
 SetArgs Args: -n -b -d "Home Videos"
 Var1 is set to:  No
 Var2 is set to:  Backup
 Directory Name: Home Videos     <-- Без двойных кавычек в конечном использовании.

Я пытался экранировать двойные кавычки, безуспешно.

Использование $* или $@ без кавычек никогда не имеет смысла.

"$*" — это объединение позиционных параметров с первым символом (или байтом в зависимости от оболочки) из $IFS, "$@" — это список позиционных параметров.

При использовании без кавычек это то же самое, но подвержено split+glob (или только удаление пустых с zsh), как и любое другое раскрытие параметров без кавычек (некоторые оболочки также разделяют аргументы в $*, даже если $IFS пуст).

Здесь вы хотите передать список аргументов как есть в вашу функцию, поэтому это:

SetArgs "$@"
[...]
Function1 "$@"

Обратите внимание, что с ksh88 $IFS должно содержать символ пробела (по умолчанию), чтобы это работало правильно (ошибка, унаследованная от Bourne shell, исправлена в ksh93).

Также обратите внимание, что с некоторыми реализациями ksh (как у старых версий zsh в эмуляции ksh),

export DirectoryName=$1

это случай вызова split+glob. export — одна из тех команд в Korn-подобных оболочках, которые могут выполнять оценки оболочки через арифметическую оценку в индексах массивов), поэтому это один из тех случаев, когда важно цитировать переменные, чтобы избежать уязвимостей инъекции команд.

Пример:

$ (exec -a ksh zsh-4.0.1 -c 'export x=$a' ksh 'foo psvar[0`uname>&2`]')
Linux

Обратите внимание, что [ $# -gt 0 ] — это еще один случай вызова split+glob, который не имеет смысла (менее вероятно, что это будет проблемой, по крайней мере, с значением по умолчанию $IFS).

Вы рассматривали возможность использования встроенной команды оболочки getopts для разбора ваших аргументов (как предложил, например, @Stéphane Chazelas в этом комментарии)? Это избавило бы вас от многих из этих хлопот.

В вашем случае, соответствующий код выглядел бы следующим образом:

while getopts "nbd:" argname
do
    case "$argname" in
    n)  Var1="No" ;;
    b)  Var2="Backup" ;;
    d)  DirectoryName ="$OPTARG" ;;
    ?)  echo -e "usage is..."
        exit 1 ;;
    esac
done

.

Ответ или решение

Ваша задача заключается в том, чтобы корректно передать параметры из командной строки в функцию оболочки с учетом наличия пробелов в значениях. Рассмотрим, как добиться желаемого результата, используя Korn Shell (ksh).

Проблемы передачи параметров в функции

Наиболее частая проблема при передаче параметров в функции возникает из-за некорректного использования $* и $@ без кавычек. Это приводит к тому, что параметры с пробелами разделяются, что ведет к нежелательным результатам.

Исправление текущего кода

Ваш текущий скрипт нехорошо обрабатывает параметры, особенно те, в которых есть пробелы, как в случае с "Home Videos". Чтобы передать параметры корректно, нужно всегда использовать кавычки. Предлагается изменить SetArgs $* на SetArgs "$@" и также вызвать Function1 "$@".

Правильное использование $@ и $*

  • "$@": передаёт каждый параметр как отдельный аргумент. Это то, что вам нужно в большинстве случаев.
  • *`"$"**: объединяет все параметры в одну строку, разделенную первым символом из$IFS`. Рекомендуется использовать крайне осторожно.

Возможные улучшения с использованием getopts

getopts — это встроенная команда в shell, позволяющая легче и безопаснее парсить параметры из командной строки. Вот пример, как это можно внедрить в ваш скрипт:

#!/bin/ksh

parse_arguments() {
    while getopts ":nbd:" opt; do
        case "$opt" in
            n)
                Var1="No"
                ;;
            b)
                Var2="Backup"
                ;;
            d)
                DirectoryName="$OPTARG"
                ;;
            ?)
                echo "Usage: $0 [-n] [-b] [-d directory]"
                exit 1
                ;;
        esac
    done
}

main() {
    parse_arguments "$@"
    echo "Var1: $Var1"
    echo "Var2: $Var2"
    echo "DirectoryName: $DirectoryName"
}

main "$@"

Итого

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

Оцените материал
Добавить комментарий

Капча загружается...