Как получить вывод функции оболочки без наложения подколонки?

У меня есть следующие функции.

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; 
}