
Может кто то подскажет в каких случаях массивы облегчают жизнь.
Модераторы: /dev/random, Модераторы разделов
При универсальной обработке произвольных аргументов. Для примера, попробуйте написать скрипт следующего вида:LittleJohn писал(а): ↑17.01.2011 19:50Может кто то подскажет в каких случаях массивы облегчают жизнь.
Код: Выделить всё
Usage: script [options] [--] command [args]
Options:
-s Run command under sudo
-t Run command under strace
-u USER Run with this user's rights. Implies sudo.
Examples:
script echo 123
Just executes `echo 123`.
script -s echo 123
Executes `sudo echo 123`.
script -u somebody -t echo 123
Executes `sudo -u somebody strace echo 123`.
В целом пример хороший, но именно для _работы_ с массивами можно добавить вариант, где нужно, скажем, перетасовать аргументы местами. А не просто один раз вставить "$@". (:watashiwa_daredeska писал(а): ↑17.01.2011 20:14При универсальной обработке произвольных аргументов. Для примера, попробуйте написать скрипт следующего вида:LittleJohn писал(а): ↑17.01.2011 19:50Может кто то подскажет в каких случаях массивы облегчают жизнь.
Соответственно, штуковина должна работать с любыми аргументами, включая содержащие символы с кодами 1..32, кавычками и прочими спец. символами shell.Код: Выделить всё
Usage: script [options] [--] command [args] Options: -s Run command under sudo -t Run command under strace -u USER Run with this user's rights. Implies sudo. Examples: script echo 123 Just executes `echo 123`. script -s echo 123 Executes `sudo echo 123`. script -u somebody -t echo 123 Executes `sudo -u somebody strace echo 123`.
такой подойдёт?watashiwa_darede... писал(а): ↑17.01.2011 20:14Для примера, попробуйте написать скрипт следующего вида
Код: Выделить всё
#!/bin/bash
unset sudo_c user_c
while getopts 'su:' flag
do
case "$flag" in
"s") sudo_c="sudo";;
"u") if [ ! "$user_c" ]
then sudo_c="sudo"; user_c="-u $OPTARG"
fi;;
esac
done
shift $((OPTIND-1))
echo $sudo_c $user_c "$@"
гхм. при вводе команды текущий шелл влезет грязными лапками и сделает подстановки. есть вариант, как это можно обойти программно?
sash-kan писал(а): ↑17.01.2011 23:18такой подойдёт?watashiwa_darede... писал(а): ↑17.01.2011 20:14Для примера, попробуйте написать скрипт следующего видав последней строке стоит echo (а не eval) для тестов:Код: Выделить всё
#!/bin/bash unset sudo_c user_c while getopts 'su:' flag do case "$flag" in "s") sudo_c="sudo";; "u") if [ ! "$user_c" ] then sudo_c="sudo"; user_c="-u $OPTARG" fi;; esac done shift $((OPTIND-1)) echo $sudo_c $user_c "$@"
$ ./script -s -u root -s a$(echo -e '\0001')b
sudo -u root ab
$ ./script -s -u root -s a$(echo -e '\0001')b | hd
00000000 73 75 64 6f 20 2d 75 20 72 6f 6f 74 20 61 01 62 |sudo -u root a.b|
00000010 0a |.|
00000011
после замены echo на eval:
$ ./script -s -u root -s a$(echo -e '\0001')b
sudo: ab: command not found
гхм. при вводе команды текущий шелл влезет грязными лапками и сделает подстановки. есть вариант, как это можно обойти программно?
А eval там вообще нельзя, ибо он непоправимо покорёжит "$@". Надо просто $sudo_c $user_c "$@".
В целом пример плохой, как показало решение sash-kan. Тут спасает то, что sudo, username и strace не содержат «нехороших» символов по определению. Если задача в склеивании большой команды из нескольких кусочков, которые задаются пользователем и могут содержать гадости, то всё становится хуже. Просто a la $sudo_c без кавычек уже не напишешь, а "$sudo_c" съедает деление на отдельные токены, а eval "$sudo_c" творит страшное без дополнительного правильного закавычивания того, что внутри. Иногда можно, конечно играть во что-нибудь вроде set -- cmd "arg 1" "arg 2" "$@", но не всегда.
При вводе можно закавычить. Я имел в виду, что если я ввел башевское $'xyz\n$(cat /etc/passwd)\n\e[31m', то оно должно таким и остаться, вместе со всеми переводами строк, знаками доллара и пр., а не развернуться где-то в потрохах скрипта.
Хуже того, $ ./script -s -u root -s 'a$(echo -e '\0001')b' выдаст то же самое, что уже не правильно. Именно потому, что eval.
Вот тут не совсем понял. Какое деление съедают кавычки? Пробелами? То, что попадёт в массив, к этому времени уже будет разделено.
Да.
Если без массивов решать задачу. Массив-то как раз эту проблему позволяет решить легко.
А, тогда понял. (: Да, без массивов та ещё работа получится. Не зря ведь, в частности, упомянутый $@ встречается в скриптах гораздо чаще, чем $*.
отличное предложение. «доберитесь до информации, хранящейся _только_ в массиве, не используя массива»
Код: Выделить всё
#!/bin/bash
c="$1"
shift
while [ "$1" ]; do
c="$c '$1'"
shift
done
eval $c
Не верю! c="$c '$1'" совершенно недостаточно, чтобы одинарная кавычка из $1 корректно обрабатывалась потом в eval.
Собственно, я к тому и говорил: пример твой вовсе не плохой, т.к. преимущество использования массивов демонстрирует. По коду sash-kan см. замечание ZyX: массив у него всё-таки есть.
С чего вдруг «только»? Она и в строке хранится: $*.
спасибо. а то сбивают с толку, понимаешь…/dev/random писал(а): ↑18.01.2011 10:53
$@ - не массив, а просто подстановка в строку всех аргументов скрипта/функции, с корректным делением на аргументы.
И, кстати, в POSIX массивов нет, а $@ - есть.
в том случае когда у вас любимый стиль такой.LittleJohn писал(а): ↑17.01.2011 19:50Может кто то подскажет в каких случаях массивы облегчают жизнь.
кстати, очень хотелось бы увидеть пример более простой и легче читаемой работы с переменной $@..watashiwa_daredeska писал(а): ↑18.01.2011 04:37Не верю! c="$c '$1'" совершенно недостаточно, чтобы одинарная кавычка из $1 корректно обрабатывалась потом в eval.
Нужно чуть сложнее: c="$c '$(echo "$1" | sed "s/'/'\\\''/g")'" Однако, никто не говорит, что массивы позволяют что-то, чего нельзя без них. Ты же понимаешь: тьюринг-полный и т.д. и т.п. Но некоторые вещи с массивами делать проще и читать потом легче, чем вот ту фигатень с sed'ом.
ну, я вот это попробовал написать без массивов:
watashiwa_darede... писал(а): ↑17.01.2011 20:14Для примера, попробуйте написать скрипт следующего вида
Слегка модифицированный пример с двумя возможными префиксными командами и аргументами к ним в свободной форме:
Код: Выделить всё
#!/bin/bash
# wrapper.sh без массивов
quote() {
echo -n "'"
echo -n "$1" | sed "s/'/'\\\''/g"
echo -n "'"
}
unset prefix_a prefix_b command
eval "set -- $(getopt -o +a:b: -- "$@")"
while :; do
case "$1" in
-a)
prefix_a="prefix_a $(quote "$2")"
shift
;;
-b)
prefix_b="prefix_b $(quote "$2")"
shift
;;
--)
shift
break
;;
esac
shift
done
command="$prefix_a $prefix_b"
for arg; do
command="$command $(quote "$arg")"
done
eval "args $command"
Код: Выделить всё
#!/bin/bash
# wrapper.sh с массивами
unset prefix_a prefix_b
eval "set -- $(getopt -o +a:b: -- "$@")"
while :; do
case "$1" in
-a)
prefix_a=(prefix_a "$2")
shift
;;
-b)
prefix_b=(prefix_b "$2")
shift
;;
--)
shift
break
;;
esac
shift
done
args "${prefix_a[@]}" "${prefix_b[@]}" "$@"
Код: Выделить всё
#!/bin/sh
# args
i=0
while [ $# -gt 0 ]; do
echo "[$i]: '$1'"
shift
i=$((i+1))
done
user@localhost
~$ ./wrapper.sh cmd
[0]: 'cmd'
~$ ./wrapper.sh $'multiline\ncmd'
[0]: 'multiline
cmd'
~$ ./wrapper.sh -a A cmd
[0]: 'prefix_a'
[1]: 'A'
[2]: 'cmd'
~$ ./wrapper.sh -a A\'B\ C$'\n'D cmd
[0]: 'prefix_a'
[1]: 'A'B C
D'
[2]: 'cmd'
~$ ./wrapper.sh -a A\'B\ C$'\n'D -b E\'F\ G$'\n'H cmd
[0]: 'prefix_a'
[1]: 'A'B C
D'
[2]: 'prefix_b'
[3]: 'E'F G
H'
[4]: 'cmd'
Если отбросить парсинг опций, то в скрипте с использованием массивов остается ровно одна строчка: та, которая последняя, а в скрипте без массивов еще функция и цикл, который надо не забыть, иначе всё пропало.
Твои примеры, если без изменений, не будет работать с аргументами из моих примеров.
то же самое останется. ведь $@ — это не массив.watashiwa_darede... писал(а): ↑19.01.2011 13:44Если отбросить парсинг опций, то в скрипте с использованием массивов остается ровно одна строчка: та, которая последняя, а в скрипте без массивов еще функция и цикл, который надо не забыть, иначе всё пропало.
Он про свои два скрипта, а не про твой. Или я тебя неправильно понял?sash-kan писал(а): ↑19.01.2011 21:36то же самое останется. ведь $@ — это не массив.watashiwa_darede... писал(а): ↑19.01.2011 13:44Если отбросить парсинг опций, то в скрипте с использованием массивов остается ровно одна строчка: та, которая последняя, а в скрипте без массивов еще функция и цикл, который надо не забыть, иначе всё пропало.
в своём первом скрипте wd зачем-то усложнил конструкцию, организовав цикл по списку аргументов. естественно, это не нужно. можно напрямую взять значение специальной переменной $@. ведь это не array, а специальная переменная (rtmb → read the man bash).t.t писал(а): ↑19.01.2011 22:20Он про свои два скрипта, а не про твой. Или я тебя неправильно понял?sash-kan писал(а): ↑19.01.2011 21:36то же самое останется. ведь $@ — это не массив.watashiwa_darede... писал(а): ↑19.01.2011 13:44Если отбросить парсинг опций, то в скрипте с использованием массивов остается ровно одна строчка: та, которая последняя, а в скрипте без массивов еще функция и цикл, который надо не забыть, иначе всё пропало.
LittleJohn писал(а): ↑17.01.2011 19:50Может кто то подскажет в каких случаях массивы облегчают жизнь.
Код: Выделить всё
# Config information for eth0:
IPADDR[0]=""
NETMASK[0]=""
USE_DHCP[0]="yes"
DHCP_HOSTNAME[0]=""
DHCP_KEEPRESOLV[0]="yes"
# Config information for eth1:
IPADDR[1]="172.16.1.1"
NETMASK[1]="255.255.255.0"
USE_DHCP[1]="no"
DHCP_HOSTNAME[1]=""
DEBUG_ETH_UP[1]="yes"
Код: Выделить всё
# Function to start the network:
start() {
lo_up
for i in ${IFNAME[@]}; do
if_up $i
done
gateway_up
if [ -x /etc/rc.d/rc.dhcpd ]; then
/etc/rc.d/rc.dhcpd start
fi
}
Нельзя. Потому что для eval нужен дополнительный уровень квотирования, а без eval аргументы в общем виде тоже не обработать.
watashiwa_daredeska писал(а): ↑20.01.2011 14:47а без eval аргументы в общем виде тоже не обработать.
/dev/random писал(а): ↑20.01.2011 14:54Почему это?
[ -n "$foo" ] && set -- foo "$foo" "$@"
[ -n "$bar" ] && set -- bar "$bar" "$@"
script "$@"
watashiwa_darede... писал(а): ↑17.01.2011 23:59Иногда можно, конечно играть во что-нибудь вроде set -- cmd "arg 1" "arg 2" "$@", но не всегда.