Как получить вывод функции оболочки без разветвления вспомогательной оболочки?

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

hello () {
        echo "Hello"
}
func () {
        hello
        echo "world"
}

Если я не хочу, чтобы вывод функции приветствия печатался, но хочу что-то с ним сделать, я хочу зафиксировать вывод в какой-либо переменной. Единственный возможный способ — разветвить подоболочку, как показано ниже? Разве это не ненужное создание нового дочернего процесса? Можно ли это оптимизировать?

func () {
        local Var=$(hello)
        echo "${Var/e/E} world"
}

person balki    schedule 21.09.2011    source источник
comment
Это может быть создание подоболочки, но почему это проблема? Вы уверены, что не оптимизируете преждевременно?   -  person evil otto    schedule 22.09.2011
comment
@evilotto Это может быть проблемой, если функция имеет побочные эффекты, помимо вывода на стандартный вывод: например, изменение переменных.   -  person ZyX    schedule 22.09.2011


Ответы (6)


Уродливое решение — временно заменить echo, чтобы оно устанавливало глобальную переменную, к которой вы можете получить доступ из своей функции:

func () {
  echo () {
    result="$@"
  }
  result=
  hello
  unset -f echo
  echo "Result is $result"
}

Я согласен, что это противно, но избегает подоболочки.

person Idelic    schedule 21.09.2011
comment
Я тоже думал в том же духе. Я могу заменить все эхо на эхо, которое делает что-то вроде этого. Ускорит ли это работу оболочки? - person balki; 21.09.2011

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

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:

person codeforester    schedule 04.03.2018

Как насчет использования дескриптора файла и строки Bash здесь?

hello () {
    exec 3<<<"Hello"
}

func () {
    local Var
    exec 3>&-
    hello && read Var <&3
    echo "${Var/e/E} world"
    exec 3>&-
}

func
person tim    schedule 22.09.2011
comment
Криптика Bash практически бесконечна, поэтому ~ 99% из нас, которые, даже будучи давними кодировщиками Bash, НЕ знают-иначе-запоминают 100% документов Bash, НА АНГЛИЙСКОМ ЯЗЫКЕ, перекодируют под названием fn, поэтому весь его вывод относится к файловому дескриптору, ДРУГОМ, чем stdout, затем манипулируйте этим (дополнительно!), в соответствии с соответствующими ОФИЦИАЛЬНЫМИ URL-адресами ДЛЯ РАСШИРЕННЫХ операций Bash gnu.org/software/bash/manual/html_node/ & gnu.org/software/bash/manual/html_node/, хотя найти там не определено кавычка (›&-) хотя я делаю это на tldp.org/LDP/abs/html/ io-redirection.html#CFD - person Destiny Architect; 11.06.2014
comment
--Передовой! Но есть ли способ (поместите здесь другой ответ) сделать это, как, по-видимому, спросил спрашивающий (я тоже очень хочу): БЕЗ необходимости перекодировать вызываемую функцию (так скажем, в этом случае функция остается hello () { echo Hello })? - но по-прежнему НЕ создает вложенную оболочку (поэтому позволяет fn выполнять видимые побочные эффекты среды и другие преимущества), поскольку вызов stdout fn по-прежнему перенаправляется на этот другой файловый дескриптор. Я предпринял такие попытки, как мой (внутренний) идентификатор N6ZPBM, где кавычка (exec hello 1>&3) (см. мой последний cmt для официальных определений этих операций), но, к сожалению, они просто дают кавычку (3: неверный файловый дескриптор). - person Destiny Architect; 11.06.2014

Ответ не bash: по крайней мере одна оболочка ksh оптимизирует подстановку команд $( ... ), чтобы не создавать подоболочку для встроенных команд. Это может быть полезно, когда ваш сценарий имеет тенденцию выполнять много таких действий.

person Henk Langeveld    schedule 04.03.2018
comment
bash, кажется, делает то же самое - person Laurence Renshaw; 06.08.2018
comment
@LaurenceRenshaw В bash 4.4.12(1) в Linux echo $(echo foo) вызывает clone(), согласно strace. Это заставляет меня думать, что он действительно порождает подоболочку. У вас есть доказательства, что это не так? - person Don Hatch; 12.02.2019
comment
Это дочерний процесс, но не полная подоболочка. Дочерний процесс не вызывает exec(), поэтому он не берет на себя все накладные расходы вложенной оболочки, но все же это отдельный процесс. - person Andrew Vickers; 18.10.2019
comment
@AndrewVickers Оболочка ksh93 не всегда разветвляется для $( ... ), поэтому новый процесс не создается. - person Henk Langeveld; 19.10.2019
comment
@Хэнк. Полезно знать для ksh, но, видимо, не для bash. - person Andrew Vickers; 19.10.2019

Есть ли у вас возможность изменить функцию 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}"
}
person Andrew Vickers    schedule 18.10.2019
comment
Собственно, зачем вообще возиться с -v. Если есть параметр, мы можем предположить, что это имя переменной. Отредактировано соответственно. - person Andrew Vickers; 18.10.2019
comment
Фантастика! Это работает и в примитивном интерпретаторе BusyBox. - person BuvinJ; 21.10.2019

Это буквально не отвечает на вопрос, но это жизнеспособный альтернативный подход для некоторых случаев использования...

Это своего рода побочный продукт @Andrew Vickers, в котором вы можете положиться на eval.

Вместо того, чтобы определять функцию, определите то, что я буду называть «макросом» (эквивалент C):

MACRO="local \$var=\"\$val world\""

func()
{ 
    local var="result"; local val="hello"; eval $MACRO; 
    echo $result; 
}
person BuvinJ    schedule 21.10.2019