sh: Безопасно ли использовать переменную в качестве команды, если команда содержит только буквы, цифры и подчеркивания?
Я пишу POSIX-совместимый скрипт в dash, поэтому мне приходится проявлять творческий подход с использованием поддельных массивов.
Содержимое fake_array.sh
fake_array_job() {
array="$1"
job_name="$2"
comma_count="$(echo "$array" | grep -o -F ',' | wc -l)"
if [ "$comma_count" -lt '1' ]; then
echo 'You gave a fake array to fake_array_job that does not contain at least one comma. Exiting...'
exit
fi
array_count="$(( comma_count + 1 ))"
position=1
while [ "$position" -le "$array_count" ]; do
item="$(echo "$array" | cut -d ',' -f "$position")"
"$job_name" || exit
position="$(( position + 1 ))"
done
}
Содержимое файла script.sh
#!/bin/sh
. fake_array.sh
job_to_do() {
echo "$item"
}
fake_array_job 'goat,pig,sheep' 'job_to_do'
second_job() {
echo "$item"
}
fake_array_job 'apple,orange' 'second_job'
Я знаю, что может показаться глупым использовать уникальное имя для каждой работы, которую я передаю fake_array_job, но мне нравится, что я должен набирать его дважды, потому что это помогает уменьшить человеческие ошибки.
Я продолжаю читать, что это плохая идея использовать переменную в качестве команды. Имеет ли мое использование "$job_name" для запуска функции какие-либо негативные последствия, касающиеся стабильности, безопасности или эффективности?
1 ответ
(Прочитайте до конца хорошее предложение Чарльза Даффи. Мне лень полностью переписать свой ответ, чтобы упомянуть об этом раньше...)
Вы можете перебирать "массив", используя простые расширения параметров, не требуя нескольких элементов в массиве.
fake_array_job() {
args=${1%,}, # Ensure the array ends with a comma
job_name=$2
while [ -n "$args" ]; do
item=${args%%,*}
"$job_name" || exit
args=${args#*,}
done
}
Одна из проблем, описанных выше, заключается в том, что этот массив завершается запятыми, предполагая, что foo,bar,
не является разделенным запятыми массивом с пустым последним элементом. Лучшее (хотя и более уродливое) решение - использовать read
разбить массив.
fake_array_job () {
args=$1
job_name=$2
rest=$args
while [ -n "$rest" ]; do
IFS=, read -r item rest <<EOF
$rest
EOF
"$job_name" || exit
done
}
(Ты можешь использовать <<-EOF
и убедитесь, что здесь документ с отступом от вкладок, но это трудно передать здесь, поэтому я просто оставлю уродливую версию.)
Есть также хорошее предложение Чарльза Даффи об использовании case
для сопоставления с образцом в массиве, чтобы увидеть, остались ли запятые или нет:
while [ -n "$args" ]; do
case $var in
*,*) next=${args%%,*}; var=${args#*,}; "$cmd" "$next";;
*) "$cmd" "$var"; break;;
esac;
done