Как получить вывод функции оболочки без наложения подколонки?
У меня есть следующие функции.
hello () {
echo "Hello"
}
func () {
hello
echo "world"
}
Если я не хочу выводить вывод функции hello, но хочу что-то сделать с ней, я хочу захватить вывод в некоторой переменной. Единственный возможный способ - развить подоболочку, как показано ниже? Разве это не лишнее создание нового дочернего процесса? Можно ли это оптимизировать?
func () {
local Var=$(hello)
echo "${Var/e/E} world"
}
Ответы
Ответ 1
Уродливое решение состоит в том, чтобы временно заменить echo
так, чтобы он задавал глобальную переменную, доступ к которой вы можете получить из вашей функции:
func () {
echo () {
result="[email protected]"
}
result=
hello
unset -f echo
echo "Result is $result"
}
Я согласен, что это противно, но избегает подоболочки.
Ответ 2
Вы можете заставить вызывающего передать имя переменной для хранения выходного значения, а затем создать глобальную переменную с этим именем внутри функции, например:
myfunc() { declare -g $1="hello"; }
Затем назовите это как:
myfunc mystring
echo "$mystring world" # gives "hello world"
Итак, ваши функции могут быть переписаны как:
hello() {
declare -g $1="Hello"
}
func() {
hello Var
echo "${Var/e/E} world"
}
Единственное ограничение - переменные, используемые для хранения выходных значений, не могут быть локальными.
Связанный пост, который говорит об использовании namerefs:
Ответ 3
Как использовать дескриптор файла и строку Bash здесь?
hello () {
exec 3<<<"Hello"
}
func () {
local Var
exec 3>&-
hello && read Var <&3
echo "${Var/e/E} world"
exec 3>&-
}
func
Ответ 4
Не ответ bash: по крайней мере одна оболочка, ksh оптимизирует подстановку команд $( ... )
, чтобы не создавать подоболочку для встроенных команд. Это может быть полезно, когда ваш script имеет тенденцию выполнять много из них.
Ответ 5
У вас есть возможность изменить функцию hello()
? Если это так, то дайте ему возможность сохранить результат в переменной:
#!/bin/bash
hello() {
local text="hello"
if [ ${#1} -ne 0 ]; then
eval "${1}='${text}'"
else
echo "${text}"
fi
}
func () {
local var # Scope extends to called functions.
hello var
echo "${var} world"
}
И более компактная версия hello()
:
hello() {
local text="hello"
[ ${#1} -ne 0 ] && eval "${1}='${text}'" || echo "${text}"
}
Ответ 6
Вы можете направить вывод одной функции в следующую.
Таким образом, вы можете объединить их в цепочку, как в типичном наборе команд, которые вы увидите в сценариях оболочки (например, используя cat
, grep
, cut
и т.д.)
Пример:
myFunc | myOtherFunc
Большая хитрость здесь заключается в чтении из стандартного, а не в получении аргумента через аргумент. Вот мой пост о том, как достичь этого аспекта к этому:
fooobar.com/questions/201025/...
Ответ 7
ТЕМП ОТВЕТ (WIP)
Попытка выработать шаблон, которым нужно следовать для вложения ответа, отправленного @Andrew Vickers...
hello() {
local text="hello"
local retVar; local retVal;
if [ $# -gt 1 ]; then
# execute the nesting
$*
# the last argument in the nesting will always be the return value...
# get the name of the last arg in the nested arg list
retVar=$#
retVar=\$\{${retVar}\}
eval "retVar=${retVar}"
# get the value of the last arg in the nested arg list
retVal=\$\{retVar\}
eval "retVal=${retVal}"
retVal=\$\{${retVal}\}
eval "retVal=${retVal}"
elif [ $# -gt 0 ]; then
retVar=${1}
fi
if [ ! -z "${retVar}" ]; then
eval "${retVar}='${text}'${retVal}"
else
echo "${text}"
fi
}
world() {
local text="world"
if [ $# -gt 0 ]; then
eval "${1}='${text}'"
else
echo "${text}"
fi
}
func () {
local var # Scope extends to called functions.
hello world var
echo "${var}"
}
Ответ 8
Это не дает буквального ответа на вопрос, но это жизнеспособный альтернативный подход для некоторых вариантов использования...
Это своего рода выход из @Andrew Vickers, в котором вы можете опираться на eval
.
Вместо того, чтобы определять функцию, определите то, что я назову "макросом" (эквивалент C):
MACRO="local \$var=\"\$val world\""
func()
{
local var="result"; local val="hello"; eval $MACRO;
echo $result;
}