Как получить последний аргумент функции a /bin /sh

Какой лучший способ реализовать print_last_arg?

#!/bin/sh

print_last_arg () {
    eval "echo \${$#}"  # this hurts
}

print_last_arg foo bar baz
# baz

(Если это были, скажем, #!/usr/bin/zsh вместо #!/bin/sh Я знаю, что делать. Моя проблема - найти разумный способ реализовать это для #!/bin/sh.)

EDIT: Вышеприведенный пример является лишь глупым. Моя цель состоит не в том, чтобы печатать последний аргумент, а скорее иметь способ ссылаться на последний аргумент внутри оболочки.


EDIT2: Приносим извинения за такой нечетко сформулированный вопрос. Надеюсь, на этот раз все будет в порядке.

Если это были /bin/zsh вместо /bin/sh, я мог бы написать что-то вроде этого

#!/bin/zsh

print_last_arg () {
    local last_arg=$argv[$#]
    echo $last_arg
}

Выражение $argv[$#] является примером того, что я описал в своем первом EDIT, как способ ссылаться на последний аргумент в рамках функции оболочки .

Поэтому я действительно должен был написать свой оригинальный пример следующим образом:

print_last_arg () {
    local last_arg=$(eval "echo \${$#}")   # but this hurts even more
    echo $last_arg
}

... чтобы было ясно, что то, что мне нужно, - это менее ужасно, чтобы поставить справа от задания.

Обратите внимание, однако, что во всех примерах последний аргумент получает доступ к неразрушающе . IOW, доступ к последнему аргументу оставляет позиционные аргументы в целом незатронутыми.

10 голосов | спросил kjo 22 Jpm1000000pmFri, 22 Jan 2016 22:20:06 +030016 2016, 22:20:06

6 ответов


1
eval printf %s${1+"'\n' \"the last arg is \${$#"\}\"}

... либо напечатает строку the last arg is , а затем <space> , значение последнего аргумента и конечная <newline> , если есть хотя бы один аргумент, или же для нулевых аргументов он вообще ничего не печатает.

Если вы это сделали:

eval ${1+"lastarg=\${$#"\}}

... то либо вы присвоите значение последнего аргумента переменной оболочки $lastarg, если есть хотя бы один аргумент , или вы ничего не сделаете. В любом случае, вы сделали бы это безопасно, и он должен быть переносимым даже для вас, как мне кажется, в корпусе Olde Bourne.

Вот еще один, который будет работать аналогичным образом, хотя для этого требуется скопировать весь массив arg дважды (и требуется printf в $PATH для оболочки Bourne) :

if   [ "${1+:}" ]
then set "$#" "[email protected]" "[email protected]"
     shift    "$1"
     printf %s\\n "$1"
     shift
fi
ответил mikeserv 23 Jam1000000amSat, 23 Jan 2016 10:32:15 +030016 2016, 10:32:15
3

С учетом примера открытия сообщения (позиционные аргументы без пробелов):

print_last_arg foo bar baz

Для значения по умолчанию IFS=' \t\n', как насчет:

args="$*" && printf '%s\n' "${args##* }"

Для более безопасного расширения "$*" установите IFS (per @ StéphaneChazelas):

( IFS=' ' args="$*" && printf '%s\n' "${args##* }" )

Но приведенное выше не будет выполнено, если ваши позиционные аргументы могут содержать пробелы. В этом случае используйте вместо этого:

for a in "[email protected]"; do : ; done && printf '%s\n' "$a"

Обратите внимание, что эти методы избегают использования eval и не имеют побочных эффектов.

Протестировано на shellcheck.net

ответил AsymLabs 23 Jam1000000amSat, 23 Jan 2016 00:16:34 +030016 2016, 00:16:34
2

Вот упрощенный способ:

print_last_arg () {
  if [ "$#" -gt 0 ]
  then
    s=$(( $# - 1 ))
  else
    s=0
  fi
  shift "$s"
  echo "$1"
}

(обновлено на основе точки @ cuonglm о том, что оригинал не прошел, когда не передал никаких аргументов, теперь это echos пустая строка - измените это поведение в else при желании)

ответил Jeff Schaller 22 Jpm1000000pmFri, 22 Jan 2016 22:29:31 +030016 2016, 22:29:31
2

Это должно работать с любой совместимой с POSIX оболочкой и будет работать также с оболочкой Solaris Bourne, предварительно установленной POSIX:

do=;for do do :;done;printf "%s\n" "$do"

и вот функция, основанная на том же подходе:

print_last_arg()
  if [ "$*" ]; then
    for do do :;done
    printf "%s\n" "$do"
  else
    echo
  fi

PS: не говорите мне, что я забыл фигурные скобки вокруг тела функции; -)

ответил jlliagre 23 Jpm1000000pmSat, 23 Jan 2016 14:35:48 +030016 2016, 14:35:48
2

Из «Unix - часто задаваемые вопросы»

(1)

unset last
if    [ $# -gt 0 ]
then  eval last=\${$#}
fi
echo  "$last"

Если число аргументов может быть равно нулю, то аргумент 0 $0 (обычно имя скрипта) будет присвоен ---- +: = 2 =: + ----. В этом причина if.

(2)

$last

(3)

unset last
for   last
do    :
done
echo  "$last"

Чтобы избежать печати пустой строки, когда нет аргументов, замените for i do third_last=$second_last second_last=$last last=$i done echo "$last" для:

echo "$last"

Количество нулевых аргументов исключается с помощью ${last+false} || echo "${last}" .

Это не точная копия того, что находится в ссылке на странице, некоторые улучшения были добавлены.

ответил 23 Jpm1000000pmSat, 23 Jan 2016 17:16:20 +030016 2016, 17:16:20
0

Это должно работать во всех системах с установленным perl (поэтому большинство UNICES):

print_last_arg () {
    printf '%s\0' "[email protected]" | perl -0ne 's/\0//;$l=$_;}{print "$l\n"'
}

Фокус в том, чтобы использовать printf, чтобы добавить \0 после каждого аргумента оболочки, а затем perl '-0, который устанавливает разделитель записи в NULL. Затем мы перебираем вход, удаляем \0 и сохраняем каждую строку с завершающим NULL как $l. Блок END (это то, что }{ is) будет выполняться после того, как все данные будут прочитаны, и напечатает последнюю «строку»: последний аргумент оболочки.

ответил terdon 23 Jpm1000000pmSat, 23 Jan 2016 14:58:32 +030016 2016, 14:58:32

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132