Как заставить `local` захватить код выхода?

11

В моем проекте у меня есть следующий фрагмент:

local output="$(bash "${1##*/}")"
echo "$?"

Это всегда печатает ноль из-за local, однако, удаление localзаставляет $?переменную вести себя правильно: что предполагает код выхода из подоболочки.

Мой вопрос: как я могу сохранить эту переменную локально, в то же время захватывая выходное значение?

Ultimate Hawk
источник
1
shellcheckне только поймает эту проблему, но и предложит решение на unix.stackexchange.com/a/281749/24718 !
Валид Хан

Ответы:

16
#!/bin/bash
thing() {
   local foo=$(asjkdh) ret="$?"
   echo "$ret"
}

Это отобразит 127правильный код ошибки «команда не найдена».

Вы можете использовать localдля определения более одной переменной. Так что я просто создаю локальную переменную RETдля захвата кода завершения подоболочки, прежде чем она завершится localуспешно и $?обнулится.

DopeGhoti
источник
Гарантируется ли, что bashвычисляет это выражение слева направо?
Макс Райд
Насколько я знаю, назначения переменных расположены по порядку, слева направо в этом контексте, да.
DopeGhoti
@MaxRied тот факт, что это работает надежно, может показаться, что да, это так. Тем не менее, я не могу найти информацию об этом ни в POSIX, ни в bashсправочном руководстве.
кошка
10
Кроме того, использование имен переменных all-caps является плохим тоном. См. Спецификацию переменных среды POSIX по адресу pubs.opengroup.org/onlinepubs/009695399/basedefs/… , в которой все имена в верхнем регистре описаны как зарезервированные для переменных, имеющих значение для оболочки или системы, и имена, содержащие как минимум один символ нижнего регистра. зарезервировано для использования в приложениях, учитывая, что переменные оболочки и переменные среды совместно используют пространство имен (поскольку в случае коллизий назначение первого может переопределить второе).
Чарльз Даффи
Комментарии не для расширенного обсуждения; этот разговор был перенесен в чат .
Тердон
27

Объявите локальную переменную перед ее назначением:

thing() {
  local output
  output="$(bash "${1##*/}")"
  echo "$?"
}

На мой взгляд, это также более читабельно, чем установка дополнительной RETпеременной. YMMV на этом, но это работает так, как вы ожидаете.

Wildcard
источник
2
Это гораздо лучше, чем использование отдельной переменной, что должно быть очевидно, если вы хотите проверить код возврата нескольких присваиваний: просто local var1 var2 ...и Боб - ваш дядя.
10
@ l0b0 Боб является моим дядей. : D
кот