Только для Баша , в ответ Кевин Литтл предлагает простой ${!#}. Проверьте это с помощью bash -c 'echo ${!#}' arg1 arg2 arg3. Для Баш , KSH и Zsh , в ответ Деннис Уильямсона предлагает ${@: -1}. Кроме того, ${*: -1}также может быть использован. Проверьте это с помощью zsh -c 'echo ${*: -1}' arg1 arg2 arg3. Но это не работает для dash , csh и tcsh .
olibre
2
${!#}В отличие ${@: -1}, также работает с расширением параметра. Вы можете проверить это с bash -c 'echo ${!#%.*}' arg1.out arg2.out arg3.out.
Arch Stanton
Ответы:
177
Это что-то вроде хака:
for last;do true;done
echo $last
Этот также довольно переносим (опять же, должен работать с bash, ksh и sh) и не сдвигает аргументы, что может быть хорошо.
Он использует тот факт, что forнеявно зацикливается на аргументах, если вы не указываете, что зацикливать, и тот факт, что переменные цикла не ограничены: они сохраняют последнее значение, которое им было задано.
В старом Solaris со старой оболочкой Bourne (не POSIX) я должен написать "напоследок" в $ @; do true; done "
mcoolive
8
@mcoolive @LaurenceGolsalves помимо того, что является более портативным, for last in "$@"; do :; doneтакже делает цель намного более ясной.
Адриан Гюнтер
1
@mcoolive: он даже работает в оболочке unix v7 bourne с 1979 года. Вы не сможете получить больше портативности, если он работает как на v7 sh, так и на POSIX sh :)
Dominik R
291
Это только для Bash:
echo "${@: -1}"
Приостановлено до дальнейшего уведомления. источник
43
Для тех (как я), которые задаются вопросом, зачем нужно место, man bash может сказать следующее:> Обратите внимание, что отрицательное смещение должно быть отделено от двоеточия хотя бы одним пробелом, чтобы не перепутать с расширением: -.
Foo
1
Я использовал это, и он ломается в MSYS2 Bash только в Windows. Bizarre.
Стивен Лу
2
Примечание: этот ответ работает для всех массивов Bash, в отличие от того, ${@:$#}который работает только на $@. Если вы хотите скопировать $@в новый массив с arr=("$@"), ${arr[@]:$#}будет неопределенным. Это потому, что $@имеет 0-й элемент, который не включен в "$@"расширения.
Мистер Лама
@ Mr.Llama: Еще одно место, которого следует избегать, $#- это перебирать массивы, так как в Bash массивы редки, и хотя в нем $#будет показано количество элементов в массиве, он не обязательно указывает на последний элемент (или элемент + 1). Другими словами, не следует делать for ((i = 0; i++; i < $#)); do something "${array[$i]}"; doneи вместо этого делать for element in "${array[@]}"; do something "$element"; doneили перебирать индексы: for index in "${!array[@]}"; do something "$index" "${array[$index]}"; doneесли вам нужно что-то делать со значениями индексов.
Приостановлено до дальнейшего уведомления.
80
$ set quick brown fox jumps
$ echo ${*:-1:1}# last argument
jumps
$ echo ${*:-1}# or simply
jumps
$ echo ${*:-2:1}# next to last
fox
Пространство необходимо, чтобы оно не интерпретировалось как значение по умолчанию .
Лучший ответ, так как он также включает в себя следующий за последним аргумент. Спасибо!
с40
1
Стивен, я не знаю, что ты сделал, чтобы попасть в штрафную, но мне нравится твоя работа здесь.
Бруно Броноски
4
да. просто лучший. все, кроме командования и последнего аргумента ${@: 1:$#-1}
Dyno Fu
1
@DynoFu спасибо за это, вы ответили на мой следующий вопрос. Таким образом, сдвиг может выглядеть следующим образом:, echo ${@: -1} ${@: 1:$#-1}где последний становится первым, а остальные сползают вниз
Майк
73
Самый простой ответ для Bash 3.0 или выше:
_last=${!#}# *indirect reference* to the $# variable# or
_last=$BASH_ARGV # official built-in (but takes more typing :)
Вот и все.
$ cat lastarg
#!/bin/bash# echo the last arg given:
_last=${!#}
echo $_last
_last=$BASH_ARGV
echo $_last
for x;do
echo $x
done
$BASH_ARGVне работает внутри функции bash (если я не делаю что-то не так).
Big McLargeHuge
1
BASH_ARGV имеет аргументы при вызове bash (или функции), а не текущий список позиционных аргументов.
Исаак
Также обратите внимание, что BASH_ARGVвы получите значение, которое было последним аргументом, а не просто «последнее значение». Например !: если вы предоставите один единственный аргумент, то вы вызовете shift, ${@:$#}ничего не произойдет (потому что вы переместили один-единственный аргумент!), Однако, BASH_ARGVвы все равно получите этот (ранее) последний аргумент.
Следующее будет работать для вас. @ Для массива аргументов. : означает в. $ # - длина массива аргументов. Итак, результатом является последний элемент:
${@:$#}
Пример:
function afunction{
echo ${@:$#} }
afunction -d -o local50#Outputs 50
Хотя пример для функции, сценарии также работают аналогичным образом. Мне нравится этот ответ, потому что он ясен и лаконичен.
Иона Браун
1
И это не хаки. Он использует явные особенности языка, а не побочные эффекты или специальные qwerks. Это должен быть принятый ответ.
musicin3d
25
Нашел это, когда пытался отделить последний аргумент от всех предыдущих. Хотя некоторые ответы получают последний аргумент, они не сильно помогают, если вам нужны все остальные аргументы. Это работает намного лучше:
В ответ Стивена Пенни немного лучше: использовать ${@: -1}для последней и ${@: -2:1}для второй последний (и так далее ...). Пример: bash -c 'echo ${@: -1}' prog 0 1 2 3 4 5 6печать 6. Чтобы остаться с этим текущим подходом AgileZebra, используйте, ${@:$#-1:1}чтобы получить второй последний . Пример: bash -c 'echo ${@:$#-1:1}' prog 0 1 2 3 4 5 6печать 5. (и ${@:$#-2:1}получить третий последний и так далее ...)
olibre
4
Ответ AgileZebra предоставляет способ получить все, кроме последних аргументов, поэтому я бы не сказал, что ответ Стивена заменяет его. Однако нет смысла использовать $((...))для вычитания 1, вы можете просто использовать ${@:1:$# - 1}.
dkasak
Спасибо dkasak. Обновлено, чтобы отразить ваше упрощение.
Смотрите этот комментарий, прикрепленный к старому идентичному ответу.
Приостановлено до дальнейшего уведомления.
1
Самое простое портативное решение, которое я вижу здесь. В этом нет проблем с безопасностью, @DennisWilliamson, эмпирически цитирование кажется правильным, если только нет способа установить $#произвольную строку (я так не думаю). eval last=\"\${$#}\"также работает и, очевидно, правильно. Не понимаю, почему цитаты не нужны.
Палек
1
Для bash , zsh , dash и ksheval last=\"\${$#}\" это хорошо. Но для CSH и Tcsh использования eval set last=\"\${$#}\". Смотрите этот пример tcsh -c 'eval set last=\"\${$#}\"; echo "$last"' arg1 arg2 arg3.
olibre
12
Вот мое решение:
довольно портативный (все POSIX sh, bash, ksh, zsh) должны работать
не сдвигает оригинальные аргументы (сдвигает копию).
Отличный ответ - короткий, портативный, безопасный. Спасибо!
Ян Лалинский
2
Это отличная идея, но у меня есть несколько предложений: Во - первых , цитирую должны быть добавлены как вокруг "$1"и "$#"(см это большой ответ unix.stackexchange.com/a/171347 ). Во-вторых, echoэто, к сожалению, непереносимо (особенно для -n), поэтому printf '%s\n' "$1"следует использовать вместо.
Joki
Благодаря @joki, я работал со многими различными Unix-системами, и я бы тоже не стал доверять echo -n, однако я не знаю ни одной системы Posix, где echo "$1"бы произошел сбой. Во всяком случае, printfдействительно более предсказуемо - обновлено.
Михал Шрайер
@ MichałŠrajer рассмотрим случай, когда «$ 1» равен «-n» или «-», например, ntharg 1 -nили ntharg 1 --может давать разные результаты в разных системах. Код, который у вас есть сейчас, безопасен!
Джоки
10
От самых старых до новых решений:
Наиболее переносимое решение, даже более старое sh(работает с пробелами и символами глобуса) (без цикла, быстрее):
eval printf "'%s\n'""\"\${$#}\""
Начиная с версии 2.01 Bash
$ set--The quick brown fox jumps over the lazy dog
$ printf '%s\n'"${!#} ${@:(-1)} ${@: -1} ${@:~0} ${!#}"
dog dog dog dog dog
Для ksh, zsh и bash:
$ printf '%s\n'"${@: -1} ${@:~0}"# the space beetwen `:`# and `-1` is a must.
dog dog
И для «рядом с последним»:
$ printf '%s\n'"${@:~1:1}"
lazy
Использование printf для обхода любых проблем с аргументами, начинающимися с тире (например -n).
Для всех оболочек и для старых sh(работает с пробелами и символами глобуса) это:
$ set--The quick brown fox jumps over the lazy dog "the * last argument"
$ eval printf "'%s\n'""\"\${$#}\""The last * argument
Или, если вы хотите установить lastпеременную:
$ eval last=\${$#}; printf '%s\n' "$last"The last * argument
shift $(($# - 1))- нет необходимости во внешней утилите. Работает в Bash, Ksh, Zsh и Dash.
Приостановлено до дальнейшего уведомления.
@ Денис: Хорошо! Я не знал о $((...))синтаксисе.
Лоуренс Гонсалвес
Вы хотели бы использовать printf '%s\n' "$1", чтобы избежать неожиданного поведения echo(например, для -n).
Joki
2
Если вы хотите сделать это неразрушающим способом, один из способов - передать все аргументы функции и вернуть последний:
#!/bin/bash
last(){if[[ $# -ne 0 ]] ; then
shift $(expr $# - 1)
echo "$1"#else#do something when no argumentsfi}
lastvar=$(last "$@")
echo $lastvar
echo "$@"
pax>./qq.sh 123 a b
b
123 a b
Если вам на самом деле все равно о сохранении других аргументов, вам это не нужно в функции, но мне трудно думать о ситуации, когда вы никогда не захотите сохранять другие аргументы, если они не были уже обработаны, в этом случае я бы использовал метод process / shift / process / shift / ... для их последовательной обработки.
Я предполагаю, что вы хотите сохранить их, потому что вы не следовали последовательному методу. Этот метод также обрабатывает случай, когда нет аргументов, возвращая "". Вы можете легко настроить это поведение, вставив закомментированное elseпредложение.
eval для косвенных ссылок - это излишество, не говоря уже о плохой практике, и огромная проблема безопасности (значение не заключено в кавычки в echo или вне $ (). Bash имеет встроенный синтаксис для косвенных ссылок, для любой переменной var: !поэтому last="${!#}"будет использовать тот же подход (косвенная ссылка на $ #) гораздо более безопасным, компактным, встроенным, вменяемым образом и правильно процитированным
MestreLion
Смотрите более чистый способ сделать то же самое в другом ответе .
Palec
Кавычки @MestreLion не нужны на RHS =.
Том Хейл
1
@TomHale: true для частного случая ${!#}, но не в целом: кавычки по-прежнему необходимы, если содержимое содержит буквенные пробелы, например last='two words'. Только $()безопасные пробелы независимо от содержания.
МестреЛион
1
После прочтения ответов выше я написал сценарий оболочки Q & D (должен работать на sh и bash) для запуска g ++ на PGM.cpp для создания исполняемого образа PGM. Предполагается, что последним аргументом в командной строке является имя файла (.cpp является необязательным), а все остальные аргументы являются опциями.
#!/bin/shif[ $# -lt 1 ]then
echo "Usage: `basename $0` [opt] pgm runs g++ to compile pgm[.cpp] into pgm"
exit 2fi
OPT=
PGM=# PGM is the last argument, all others are considered optionsfor F;do OPT="$OPT $PGM"; PGM=$F;done
DIR=`dirname $PGM`
PGM=`basename $PGM .cpp`# put -o first so it can be overridden by -o specified in OPTset-x
g++-o $DIR/$PGM $OPT $DIR/$PGM.cpp
Я подозреваю, что это потерпит неудачу с ./arguments.sh "last value"
Томас
Спасибо за проверку, Томас, я пытался выполнить сценарий as, как вы подразумевали # ./arguments.sh "last value" Последний аргумент: значение работает нормально. # ./arguments.sh "проверка последнего значения с двойным" Последний аргумент: double
Ranjithkumar T
Проблема в том, что последний аргумент был «последним значением», а не значением. Ошибка вызвана аргументом, содержащим пробел.
Томас
0
Существует гораздо более краткий способ сделать это. Аргументы скрипта bash могут быть перенесены в массив, что значительно упрощает работу с элементами. Скрипт ниже всегда будет печатать последний аргумент, переданный скрипту.
argArray=("$@")# Add all script arguments to argArray
arrayLength=${#argArray[@]}# Get the length of the array
lastArg=$((arrayLength -1))# Arrays are zero based, so last arg is -1
echo ${argArray[$lastArg]}
Это работает, только если последний аргумент не содержит пробела.
Palec
Ваша предварительная проверка завершается неудачно, если последний аргумент - это предложение, заключенное в двойные кавычки, причем указанное предложение должно быть одним аргументом.
Стефан
0
Чтобы вернуть последний аргумент последней использованной команды, используйте специальный параметр:
$_
В этом случае он будет работать, если он используется в сценарии до вызова другой команды.
${!#}
. Проверьте это с помощьюbash -c 'echo ${!#}' arg1 arg2 arg3
. Для Баш , KSH и Zsh , в ответ Деннис Уильямсона предлагает${@: -1}
. Кроме того,${*: -1}
также может быть использован. Проверьте это с помощьюzsh -c 'echo ${*: -1}' arg1 arg2 arg3
. Но это не работает для dash , csh и tcsh .${!#}
В отличие${@: -1}
, также работает с расширением параметра. Вы можете проверить это сbash -c 'echo ${!#%.*}' arg1.out arg2.out arg3.out
.Ответы:
Это что-то вроде хака:
Этот также довольно переносим (опять же, должен работать с bash, ksh и sh) и не сдвигает аргументы, что может быть хорошо.
Он использует тот факт, что
for
неявно зацикливается на аргументах, если вы не указываете, что зацикливать, и тот факт, что переменные цикла не ограничены: они сохраняют последнее значение, которое им было задано.источник
true
является частью POSIX .for last in "$@"; do :; done
также делает цель намного более ясной.Это только для Bash:
источник
${@:$#}
который работает только на$@
. Если вы хотите скопировать$@
в новый массив сarr=("$@")
,${arr[@]:$#}
будет неопределенным. Это потому, что$@
имеет 0-й элемент, который не включен в"$@"
расширения.$#
- это перебирать массивы, так как в Bash массивы редки, и хотя в нем$#
будет показано количество элементов в массиве, он не обязательно указывает на последний элемент (или элемент + 1). Другими словами, не следует делатьfor ((i = 0; i++; i < $#)); do something "${array[$i]}"; done
и вместо этого делатьfor element in "${array[@]}"; do something "$element"; done
или перебирать индексы:for index in "${!array[@]}"; do something "$index" "${array[$index]}"; done
если вам нужно что-то делать со значениями индексов.Пространство необходимо, чтобы оно не интерпретировалось как значение по умолчанию .
Обратите внимание, что это только для bash.
источник
${@: 1:$#-1}
echo ${@: -1} ${@: 1:$#-1}
где последний становится первым, а остальные сползают внизСамый простой ответ для Bash 3.0 или выше:
Вот и все.
Выход:
источник
$BASH_ARGV
не работает внутри функции bash (если я не делаю что-то не так).BASH_ARGV
вы получите значение, которое было последним аргументом, а не просто «последнее значение». Например !: если вы предоставите один единственный аргумент, то вы вызовете shift,${@:$#}
ничего не произойдет (потому что вы переместили один-единственный аргумент!), Однако,BASH_ARGV
вы все равно получите этот (ранее) последний аргумент.Используйте индексирование в сочетании с длиной:
Обратите внимание, что это только для bash.
источник
echo ${@:$#}
Следующее будет работать для вас. @ Для массива аргументов. : означает в. $ # - длина массива аргументов. Итак, результатом является последний элемент:
Пример:
Обратите внимание, что это только для bash.
источник
Нашел это, когда пытался отделить последний аргумент от всех предыдущих. Хотя некоторые ответы получают последний аргумент, они не сильно помогают, если вам нужны все остальные аргументы. Это работает намного лучше:
Обратите внимание, что это только для bash.
источник
${@: -1}
для последней и${@: -2:1}
для второй последний (и так далее ...). Пример:bash -c 'echo ${@: -1}' prog 0 1 2 3 4 5 6
печать6
. Чтобы остаться с этим текущим подходом AgileZebra, используйте,${@:$#-1:1}
чтобы получить второй последний . Пример:bash -c 'echo ${@:$#-1:1}' prog 0 1 2 3 4 5 6
печать5
. (и${@:$#-2:1}
получить третий последний и так далее ...)$((...))
для вычитания 1, вы можете просто использовать${@:1:$# - 1}
.Это работает во всех POSIX-совместимых оболочках:
Источник: http://www.faqs.org/faqs/unix-faq/faq/part2/section-12.html.
источник
$#
произвольную строку (я так не думаю).eval last=\"\${$#}\"
также работает и, очевидно, правильно. Не понимаю, почему цитаты не нужны.eval last=\"\${$#}\"
это хорошо. Но для CSH и Tcsh использованияeval set last=\"\${$#}\"
. Смотрите этот примерtcsh -c 'eval set last=\"\${$#}\"; echo "$last"' arg1 arg2 arg3
.Вот мое решение:
злоeval
Код:
источник
"$1"
и"$#"
(см это большой ответ unix.stackexchange.com/a/171347 ). Во-вторых,echo
это, к сожалению, непереносимо (особенно для-n
), поэтомуprintf '%s\n' "$1"
следует использовать вместо.echo -n
, однако я не знаю ни одной системы Posix, гдеecho "$1"
бы произошел сбой. Во всяком случае,printf
действительно более предсказуемо - обновлено.ntharg 1 -n
илиntharg 1 --
может давать разные результаты в разных системах. Код, который у вас есть сейчас, безопасен!От самых старых до новых решений:
Наиболее переносимое решение, даже более старое
sh
(работает с пробелами и символами глобуса) (без цикла, быстрее):Начиная с версии 2.01 Bash
Для ksh, zsh и bash:
И для «рядом с последним»:
Использование printf для обхода любых проблем с аргументами, начинающимися с тире (например
-n
).Для всех оболочек и для старых
sh
(работает с пробелами и символами глобуса) это:Или, если вы хотите установить
last
переменную:И для «рядом с последним»:
источник
Если вы используете Bash> = 3.0
источник
echo ${BASH_ARGV[1]}
ARGV
расшифровывается как «вектор аргумента».Для
bash
, этот комментарий предложил очень элегантна:В качестве бонуса, это также работает в
zsh
.источник
Это сдвигает аргументы на количество аргументов минус 1 и возвращает первый (и единственный) оставшийся аргумент, который будет последним.
Я тестировал только в bash, но он также должен работать в sh и ksh.
источник
shift $(($# - 1))
- нет необходимости во внешней утилите. Работает в Bash, Ksh, Zsh и Dash.$((...))
синтаксисе.printf '%s\n' "$1"
, чтобы избежать неожиданного поведенияecho
(например, для-n
).Если вы хотите сделать это неразрушающим способом, один из способов - передать все аргументы функции и вернуть последний:
Если вам на самом деле все равно о сохранении других аргументов, вам это не нужно в функции, но мне трудно думать о ситуации, когда вы никогда не захотите сохранять другие аргументы, если они не были уже обработаны, в этом случае я бы использовал метод process / shift / process / shift / ... для их последовательной обработки.
Я предполагаю, что вы хотите сохранить их, потому что вы не следовали последовательному методу. Этот метод также обрабатывает случай, когда нет аргументов, возвращая "". Вы можете легко настроить это поведение, вставив закомментированное
else
предложение.источник
Для tcsh:
Я вполне уверен, что это было бы портативное решение, за исключением задания.
источник
Я нашел ответ @ AgileZebra (плюс комментарий @ starfry) наиболее полезным, но он задает
heads
скаляр. Массив, вероятно, более полезен:Обратите внимание, что это только для bash.
источник
echo "${heads[-1]}"
печатает последний элемент вheads
. Или я что-то упустил?Решение с использованием
eval
:источник
!
поэтомуlast="${!#}"
будет использовать тот же подход (косвенная ссылка на $ #) гораздо более безопасным, компактным, встроенным, вменяемым образом и правильно процитированным=
.${!#}
, но не в целом: кавычки по-прежнему необходимы, если содержимое содержит буквенные пробелы, напримерlast='two words'
. Только$()
безопасные пробелы независимо от содержания.После прочтения ответов выше я написал сценарий оболочки Q & D (должен работать на sh и bash) для запуска g ++ на PGM.cpp для создания исполняемого образа PGM. Предполагается, что последним аргументом в командной строке является имя файла (.cpp является необязательным), а все остальные аргументы являются опциями.
источник
Следующее будет устанавливать
LAST
последний аргумент без изменения текущей среды:Если другие аргументы больше не нужны и их можно сместить, их можно упростить до:
По причинам переносимости следующее:
можно заменить на:
Заменив также
$()
обратными кавычками, мы получим:источник
Теперь мне просто нужно добавить текст, потому что мой ответ был слишком коротким для публикации. Мне нужно добавить больше текста для редактирования.
источник
$argv
но нет$#argv
-$argv[(count $argv)]
работает в рыбе).Это часть моей функции копирования:
Чтобы использовать в скриптах, сделайте это:
Пояснение (сначала вложенное):
$(echo '$'"$#")
возвращает$[nr]
где[nr]
число параметров. Например, строка$123
(нерасширенная).echo $123
возвращает значение 123-го параметра при оценке.eval
просто расширяется$123
до значения параметра, напримерlast_arg
. Это интерпретируется как строка и возвращается.Работает с Bash с середины 2015 года.
источник
источник
Попробуйте приведенный ниже скрипт, чтобы найти последний аргумент
Вывод:
Спасибо.
источник
Существует гораздо более краткий способ сделать это. Аргументы скрипта bash могут быть перенесены в массив, что значительно упрощает работу с элементами. Скрипт ниже всегда будет печатать последний аргумент, переданный скрипту.
Образец вывода
источник
Используя расширение параметра (удалить соответствующее начало):
Также легко получить все до последнего:
источник
Чтобы вернуть последний аргумент последней использованной команды, используйте специальный параметр:
В этом случае он будет работать, если он используется в сценарии до вызова другой команды.
источник
Просто используйте
!$
.источник