eval
единственный портативный путь состоит в том, чтобы получить доступ к позиционному параметру своим динамично выбранным положением. Ваш сценарий был бы более четким, если бы Вы явно циклично выполнились на индексе, а не значениях (который Вы не используете). Обратите внимание, что Вам не нужно expr
если Вы не хотите, чтобы Ваш сценарий работал в старинных Оболочках Bourne; $((…))
арифметика находится в POSIX. Ограничьте использование eval
к самому маленькому фрагменту; например, не использовать eval echo
, присвойте значение временной переменной.
i=$#
while [ "$i" -gt 0 ]; do
if [ "$i" -ne 3 ] && [ "$i" -ne 2 ]; then
eval "value=\${$i}"
echo "Parameter $i is $value"
fi
i=$((i-1))
done
В ударе можно использовать ${!i}
означать значение параметра, имя которого $i
. Это работает когда $i
или именованный параметр или число (обозначение позиционного параметра). В то время как Вы в нем, можно использовать другие удобные функции удара.
for ((i=$#; i>0; i--)); do
if ((i != 3 && i != 4)); then
echo "Parameter $i is ${!i}"
fi
done
Это - корректное и неопасное использование оценки. Вы полностью управляете содержанием, которое Вы eval
луг.
Если это все еще дает Вам плохо чувства, то, если Вы не заботитесь о мобильности, можно использовать Bash ${!i}
косвенный синтаксис.
Я сохраняю сценарий reverse
на моем пути, который делает это:
#!/bin/sh
if [ "$#" -gt 0 ]; then
arg=$1
shift
reverse "$@"
printf '%s\n' "$arg"
fi
Использование в качестве примера:
$ reverse a b c '*' '-n'
-n
*
c
b
a
Можно также использовать функцию вместо специализированного сценария.
$#
? это может быть что-нибудь кроме неотрицательного целого числа?
– G-Man Says 'Reinstate Monica'
12.06.2015, 20:05
declare -a argv=( "$@" )
for (( i=$((${#argv[@]}-1)) ; i>=0 ; i=$(($i-1)) )) ; do
echo "${argv[$i]}"
# use "${argv[$i]}" in here...
done
С zsh
:
$ set a 'foo bar' c '' '*'
$ printf '<%s>\n' "${(Oa)@}"
<*>
<>
<c>
<foo bar>
<a>
Oa
- это флаг раскрытия параметра для сортировки элементов массива при раскрытии в обратных индексах массива.
Чтобы исключить 3 и 4:
$ printf '<%s>\n' "${(Oa)@[5,-1]}" "${(Oa)@[1,2]}"
<*>
<foo bar>
<a>
Предполагая, что параметры позиционирования не содержат символов новой строки:
[ "$#" -gt 0 ] && printf '%s\n' "$@" | #print in order
sed '3,4 d' | #skip 3 and 4
tac #reverse (try tail -r on
#non-GNU systems).
Test:
set 1 2 3 4 5 6
printf '%s\n' "$@" |
sed '3,4 d' |
tac
Test output:
6
5
2
1
Практически с любой оболочкой:
printf '{ PS4=\${$(($#-$x))}; } 2>&3; 2>&1\n%.0s' |
x=LINENO+1 sh -sx "$@" 3>/dev/null
И вам не требуется для использования подоболочки. Например:
set -x a b c
{ last= PS4=\${last:=\${$#}}; set +x; } 2>/dev/null
echo "$last"
... печатает ...
c
А вот функция оболочки, которая может установить для вас псевдоним оболочки
, который будет печатать аргументы вперед или назад:
tofro() case $1 in (*[!0-9]*|'') ! :;;(*) set "$1"
until [ "$1" -eq "$(($#-1))" ] &&
shift && alias args=":; printf \
\"%.\$((\$??\${#*}:0))s%.\$((!\$??\${#*}:0))s\n\" $* "
do [ "$#" -gt 1 ] &&
set "$@ \"\${$#}\" " '"${'"$((1+$1-$#))"'}"' ||
set "$1" '"$1" "${'"$1"'}"'
done; esac
Это не пытается сохранить буквальные значения для каких-либо аргументов, а помещает такую строку в псевдоним args
:
:;printf "%.$(($??${#*}:0))s%.$((!$??${#*}:0))s\n" \
"$1" "${3}" "${2}" "${2}" "${3}" "${1}"
... и поэтому сохраняет только ссылки на параметры в обратном порядке и вперед. Он будет хранить до счетчика, заданного в качестве аргумента.Таким образом, указанный выше псевдоним
был сгенерирован следующим образом:
tofro 3
printf
влияет на поведение на основе возвращаемого значения предыдущей команды, которое всегда является :
нулевой командой. , и так обычно верно. printf
будет пропускать половину своих аргументов при каждой печати, что по умолчанию приводит к распечатке аргументов от наименьшего до наибольшего. Однако, если вы просто выполните:
! args
... он напечатает их в обратном порядке.
Поскольку псевдоним не хранит никаких литеральных значений, его значение остается статическим, в то время как фактические аргументы могут измениться, но он по-прежнему будет ссылаться на любое их количество. Например:
set one two three
tofro 3
args; ! args
shift; args; ! args
... который печатает ...
one
two
three
three
two
one
two
three
three
two
Но сброс псевдонима может быть выполнен как:
tofro 2
args; ! args
... и поэтому он печатает ...
two
three
three
two
arg
поскольку им заказывают правильно и не наоборот. К использованиюexpr
, Я ограничен для использования стандарта только. – WarrenFaith 25.09.2011, 19:07#!/bin/bash
, можно использовать${!i}
и(())
. Если Вы хотите придерживаться стандарта sh, они не доступны, но$((…))
. – Gilles 'SO- stop being evil' 25.09.2011, 19:10eval
не единственный портативный путь, поскольку Ryan показал. – Stéphane Chazelas 12.06.2015, 18:04