Вопрос или проблема
У меня есть скрипт, которому я передаю аргументы/опции/параметры через командную строку. Одно из значений содержит пробел, поэтому я заключил его в двойные кавычки. Возможно, будет легче предоставить пример. Простите за использование аргументов/опций/параметров.
$: ./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
не только делает код более читабельным и поддерживаемым, но и снижает вероятность возникновения ошибок из-за неверной обработки аргументов с пробелами. Это наилучший способ обеспечить безопасность и надежность при обработке параметров в вашем скрипте. Старайтесь всегда заключать переменные в кавычки, чтобы избежать неожиданного поведения, тем более когда вы работаете с параметрами, которые могут содержать пробелы или другие специальные символы.