Bash 함수에서 문자열 값을 반환하는 방법


461

Bash 함수에서 문자열을 반환하고 싶습니다.

Java로 예제를 작성하여 내가하고 싶은 것을 보여줄 것입니다.

public String getSomeString() {
  return "tadaa";
}

String variable = getSomeString();

아래 예제는 bash에서 작동하지만 더 좋은 방법이 있습니까?

function getSomeString {
   echo "tadaa"
}

VARIABLE=$(getSomeString)

4
따로, function funcName {초기 ksh에서 상속 된 POSIX 이전 레거시 구문이 있습니다 (bash는 의미 상 차이점이 있음). funcName() {더와 함께 function, 대신 사용되어서는 안된다; wiki.bash-hackers.org/scripting/obsolete
Charles Duffy

이 링크는 NAME () COMPOUND-CMD 또는 함수 NAME {CMDS; } function myFunction { blah; }그렇습니다. 그건 function myFunction() { blah }그 즉 키워드 기능 괄호의 사용, 잘되지 않습니다.
Will

답변:


290

내가 아는 더 좋은 방법은 없습니다. Bash는 상태 코드 (정수)와 stdout에 기록 된 문자열 만 알고 있습니다.


15
+1 @ tomas-f :이 함수 "getSomeString ()"에있는 코드에주의를 기울여야합니다. 결과적으로 에코되는 코드가 있으면 리턴 문자열이 잘못 될 것입니다.
Mani

11
이것은 명백한 잘못입니다. 반환 변수 내에서 임의의 데이터를 반환 할 수 있습니다. 분명히 더 좋은 방법입니다.
Evi1M4chine 2016 년

36
@ Evi1M4chine, 음 ... 안돼. 스크립트에서 볼 수 있듯이 전역 변수를 설정하고 "return"이라고 부를 수 있습니다. 그러나 그것은 실제로 규칙적으로 코드 실행과 프로그래밍 방식으로 연결되지 않습니다. "분명히 더 나은 방법"? 음 .. 아니야. 명령 대체는 훨씬 더 명확하고 모듈화되어 있습니다.
와일드 카드

6
질문이 명령에 관한 것이면 "명령 대체가 훨씬 더 명확하고 모듈화되어있다". 이 질문은 bash 함수에서 문자열을 반환하는 방법입니다! Bash 4.3 (2014?) 이후 OP가 요청한 것을 수행하는 기본 제공 방법을 사용할 수 있습니다. 아래 답변을 참조하십시오.
zenaan

4
원래 질문에는 가장 간단한 방법이 포함되어 있으며 대부분의 경우 잘 작동합니다. Bash 반환 값은 스크립팅의 표준 반환 값과 같지 않고 숫자 셸 명령 종료 코드와 유사하기 때문에 "반환 코드"라고 somefunction && echo 'success'합니다. 함수를 다른 명령처럼 생각하면 의미가 있습니다. 명령은 상태 코드 이외의 종료시 어떤 것도 "반환"하지 않지만 그 동안 캡처 할 수있는 것을 출력 할 수 있습니다.
Beejor

193

함수가 변수를 첫 번째 인수로 사용하고 반환하려는 문자열로 변수를 수정할 수 있습니다.

#!/bin/bash
set -x
function pass_back_a_string() {
    eval "$1='foo bar rab oof'"
}

return_var=''
pass_back_a_string return_var
echo $return_var

"foo bar rab oof"를 인쇄합니다.

편집 : 문자열의 공백이 @Luca Borrione의 의견을 처리 할 수 ​​있도록 적절한 위치에 인용을 추가했습니다.

편집 : 데모로 다음 프로그램을 참조하십시오. 이것은 범용 솔루션입니다. 문자열을 로컬 변수로받을 수도 있습니다.

#!/bin/bash
set -x
function pass_back_a_string() {
    eval "$1='foo bar rab oof'"
}

return_var=''
pass_back_a_string return_var
echo $return_var

function call_a_string_func() {
     local lvar=''
     pass_back_a_string lvar
     echo "lvar='$lvar' locally"
}

call_a_string_func
echo "lvar='$lvar' globally"

인쇄합니다 :

+ return_var=
+ pass_back_a_string return_var
+ eval 'return_var='\''foo bar rab oof'\'''
++ return_var='foo bar rab oof'
+ echo foo bar rab oof
foo bar rab oof
+ call_a_string_func
+ local lvar=
+ pass_back_a_string lvar
+ eval 'lvar='\''foo bar rab oof'\'''
++ lvar='foo bar rab oof'
+ echo 'lvar='\''foo bar rab oof'\'' locally'
lvar='foo bar rab oof' locally
+ echo 'lvar='\'''\'' globally'
lvar='' globally

편집 : 원래 변수의 값이 있음을 보여주는 것입니다 기능에서 사용할 수있는, 같은 잘못 코멘트에 @Xichen 리튬 비판했다.

#!/bin/bash
set -x
function pass_back_a_string() {
    eval "echo in pass_back_a_string, original $1 is \$$1"
    eval "$1='foo bar rab oof'"
}

return_var='original return_var'
pass_back_a_string return_var
echo $return_var

function call_a_string_func() {
     local lvar='original lvar'
     pass_back_a_string lvar
     echo "lvar='$lvar' locally"
}

call_a_string_func
echo "lvar='$lvar' globally"

이것은 출력을 제공합니다.

+ return_var='original return_var'
+ pass_back_a_string return_var
+ eval 'echo in pass_back_a_string, original return_var is $return_var'
++ echo in pass_back_a_string, original return_var is original return_var
in pass_back_a_string, original return_var is original return_var
+ eval 'return_var='\''foo bar rab oof'\'''
++ return_var='foo bar rab oof'
+ echo foo bar rab oof
foo bar rab oof
+ call_a_string_func
+ local 'lvar=original lvar'
+ pass_back_a_string lvar
+ eval 'echo in pass_back_a_string, original lvar is $lvar'
++ echo in pass_back_a_string, original lvar is original lvar
in pass_back_a_string, original lvar is original lvar
+ eval 'lvar='\''foo bar rab oof'\'''
++ lvar='foo bar rab oof'
+ echo 'lvar='\''foo bar rab oof'\'' locally'
lvar='foo bar rab oof' locally
+ echo 'lvar='\'''\'' globally'
lvar='' globally

1
이 답변은 훌륭합니다! C ++의 아이디어와 유사한 참조로 매개 변수를 전달할 수 있습니다.
Yun Huang

4
해당 답변에 대한 전문가의 답변을받는 것이 좋습니다. 스크립트에서 사용되는 것을 본 적이 없습니다. 어쨌든 : 그것은 +1입니다 정답에 투표해야합니다
John

이것은 fgm간단한 답변으로 작성된 답변 과 동일하지 않습니까? 문자열 foo에 공백이 있으면 작동하지 않지만 표시되는 것처럼 공백 은 작동하지 않습니다 fgm.
Luca Borrione

4
@XichenLi : downvote에 의견을 남겨 주셔서 감사합니다. 내 편집 내용을 참조하십시오. 로 함수에서 변수의 초기 값을 얻을 수 있습니다 \$$1. 다른 것을 찾고 있다면 알려주십시오.
bstpierre

1
@timiscoding 그것은로 고칠 수 있습니다 printf '%q' "$var". % q는 쉘 이스케이프를위한 형식 문자열입니다. 그런 다음 그냥 그대로 전달하십시오.
bb010g

99

위의 모든 답변은 bash의 맨 페이지에 명시된 내용을 무시합니다.

  • 함수 안에서 선언 된 모든 변수는 호출 환경과 공유됩니다.
  • local로 선언 된 모든 변수는 공유되지 않습니다.

예제 코드

#!/bin/bash

f()
{
    echo function starts
    local WillNotExists="It still does!"
    DoesNotExists="It still does!"
    echo function ends
}

echo $DoesNotExists #Should print empty line
echo $WillNotExists #Should print empty line
f                   #Call the function
echo $DoesNotExists #Should print It still does!
echo $WillNotExists #Should print empty line

그리고 출력

$ sh -x ./x.sh
+ echo

+ echo

+ f
+ echo function starts 
function starts
+ local 'WillNotExists=It still does!'
+ DoesNotExists='It still does!'
+ echo function ends 
function ends
+ echo It still 'does!' 
It still does!
+ echo

또한 pdksh 및 ksh 에서이 스크립트는 동일합니다!


10
이 답변에는 장점이 있습니다. 함수에서 문자열을 반환하고 싶다고 생각하면서 여기에 왔습니다. 이 답변을 통해 C #이 말하는 것만 알 수있었습니다. 다른 사람들도 같은 경험을 할 것으로 생각됩니다.
LOAS

4
@ElmarZander 당신은 틀 렸습니다. 이것은 전적으로 관련이 있습니다. 이것은 전역 범위에 함수 범위 값을 얻는 간단한 방법이며, 일부는 bstpierre에 의해 설명 된 전역 변수를 재정의하는 평가 방법보다 낫거나 간단하다고 생각합니다.
KomodoDave

local은 bash가 아닌 스크립트에 이식 가능하지 않으므로 일부 사람들은이를 피할 수 있습니다.
밝게 돈

질문 : 루프의 변수는 어떻습니까?
anu

1
Mac ($ bash --version GNU bash, 버전 3.2.57 (1) -release (x86_64-apple-darwin14) Copyright (C) 2007 Free Software Foundation, Inc.)에서 일치하는 전역 변수는 다음과 같습니다. 초기화되었지만 다른 함수 f2에서 동일한 변수를 부작용으로 만들려고 할 때 그 부작용은 지속되지 않습니다. 따라서 매우 일관성이 없어서 사용에 좋지 않습니다.
AnneTheAgile

45

Bash 4.3 이후 버전 인 feb 2014 (?)는 "eval"이외의 참조 변수 또는 이름 참조 (namerefs)에 대해 동일한 유리한 성능 및 간접 효과를 제공하며 스크립트에서 더 명확하고 더 어려울 수 있습니다. "eval"을 잊고이 오류를 수정해야합니다 ":

declare [-aAfFgilnrtux] [-p] [name[=value] ...]
typeset [-aAfFgilnrtux] [-p] [name[=value] ...]
  Declare variables and/or give them attributes
  ...
  -n Give each name the nameref attribute, making it a name reference
     to another variable.  That other variable is defined by the value
     of name.  All references and assignments to name, except for
     changing the -n attribute itself, are performed on the variable
     referenced by name's value.  The -n attribute cannot be applied to
     array variables.
...
When used in a function, declare and typeset make each name local,
as with the local command, unless the -g option is supplied...

그리고 또한:

파라미터

nameref 또는 다른 변수에 대한 참조를 작성하기 위해 -n 옵션을 사용하여 선언 또는 로컬 내장 명령 (아래의 선언 및 로컬 설명 참조)에 변수에 nameref 속성을 지정할 수 있습니다. 이를 통해 변수를 간접적으로 조작 할 수 있습니다. nameref 변수가 참조되거나 할당 될 때마다 실제로 nameref 변수의 값으로 지정된 변수에 대해 작업이 수행됩니다. nameref는 일반적으로 쉘 함수 내에서 함수 이름을 인수로 전달한 변수를 나타내는 데 사용됩니다. 예를 들어, 변수 이름이 첫 번째 인수로 쉘 함수에 전달되면

      declare -n ref=$1

함수 내에서 nameref 변수 ref가 생성되며 그 값은 첫 번째 인수로 전달 된 변수 이름입니다. 참조에 대한 참조 및 지정은 이름이 $ 1로 전달 된 변수에 대한 참조 및 지정으로 처리됩니다. for 루프의 제어 변수에 nameref 속성이있는 경우 단어 목록은 쉘 변수 목록이 될 수 있으며 루프가 실행될 때 목록의 각 단어에 대해 이름 참조가 설정됩니다. 배열 변수에는 -n 속성을 부여 할 수 없습니다. 그러나 nameref 변수는 배열 변수 및 아래 첨자 배열 변수를 참조 할 수 있습니다. -n 옵션을 사용하여 unset 내장에 Nameref를 설정 해제 할 수 있습니다. 그렇지 않으면, nameref 변수의 이름을 인수로하여 unset을 실행하면,

예를 들어 ( 편집 2 : (Ron 덕분에) 함수 내부 변수 이름을 네임 스페이스 (접두어 붙임)하여 외부 변수 충돌을 최소화하고 결국 Karsten의 의견에서 제기 된 문제에 올바르게 응답해야합니다.

# $1 : string; your variable to contain the return value
function return_a_string () {
    declare -n ret=$1
    local MYLIB_return_a_string_message="The date is "
    MYLIB_return_a_string_message+=$(date)
    ret=$MYLIB_return_a_string_message
}

이 예제를 테스트 :

$ return_a_string result; echo $result
The date is 20160817

bash "declare"내장은 함수에서 사용될 때 기본적으로 선언 된 변수를 "local"로 만들고 "-n"은 "local"과 함께 사용할 수도 있습니다.

"중요한 선언"변수를 "지루한 로컬"변수와 구별하는 것을 선호하므로 이런 방식으로 "declare"및 "local"을 사용하면 문서로 작동합니다.

편집 1- (Karsten의 아래 의견에 대한 답변)-더 이상 아래에 의견을 추가 할 수는 없지만 Karsten의 의견에 대한 생각이 생겼습니다. 따라서 다음과 같은 테스트를 수행했습니다. 다음 단계는 제대로 작동하기 때문에 명령 줄에서 테스트 단계를 수행하여 존재한다고 가정하는 문제를 보여줍니다.

$ return_a_string ret; echo $ret
The date is 20170104

(위의 함수를 bash 용어에 붙여 넣은 후에 지금 막 실행했습니다. 보시다시피 결과는 정상적으로 작동합니다.)


4
이것이 정상에 스며 들기를 희망합니다. eval은 최후의 수단이어야합니다. nameref 변수는 bash 4.3 ( changelog 에 따라) 이후에만 사용할 수 있습니다 (feb 2014 [?]에서 발표 됨). 이식성이 중요한 경우에 중요합니다. declare함수 내에서 로컬 변수 를 생성 한다는 사실에 대한 bash 매뉴얼을 인용하십시오 (정보는에 의해 제공되지 않음 help declare). g 옵션이 제공됩니다 ... "
init_js

2
이것은 평가 솔루션과 동일한 앨리어싱 문제가 있습니다. 함수를 호출하고 출력 변수의 이름을 전달할 때는 호출하는 함수 내에서 로컬로 사용되는 변수의 이름을 전달하지 않아야합니다. 함수 호출자가 출력 매개 변수에 해당 이름을 사용하려는 경우가 아니면 함수에서 새 로컬 변수를 추가하거나 이름을 바꿀 수 없으므로 캡슐화 측면에서 중요한 문제입니다.
Karsten

1
@Karsten은 동의했다. 두 경우 모두 (eval 및 namerefs) 다른 이름을 선택해야 할 수도 있습니다. eval에 대한 nameref 접근법의 한 가지 장점은 이스케이프 문자열을 처리 할 필요가 없다는 것입니다. 물론 항상 같은 작업을 수행 할 수 K=$1; V=$2; eval "$A='$V'";있지만 한 가지 실수 (예 : 비어 있거나 생략 된 매개 변수)가 있으면 더 위험 할 수 있습니다. @zenaan @Karsten이 제기 한 문제는 "ret"대신 "message"를 반환 변수 이름으로 선택하면 적용됩니다.
init_js

3
함수는 처음부터 nameref 인수를 허용하도록 설계되어야하므로 함수 작성자는 이름 충돌 가능성을 알고 있어야하며 일반적인 규칙을 사용하여이를 피할 수 있습니다. 예를 들어, 함수 X 내부에서 규칙 "X_LOCAL_name"을 사용하여 로컬 변수 이름을 지정하십시오.
Ron Burk

34

위의 bstpierre 와 마찬가지로 출력 변수 이름을 명시 적으로 사용하는 것이 좋습니다.

function some_func() # OUTVAR ARG1
{
   local _outvar=$1
   local _result # Use some naming convention to avoid OUTVARs to clash
   ... some processing ....
   eval $_outvar=\$_result # Instead of just =$_result
}

$를 인용하는 것에주의하십시오. 이것은 $result쉘 특수 문자로 내용을 해석하지 않도록 합니다. 나는 이것이 에코를 포착하는 관용구 보다 훨씬 더 빠른 것으로 나타났습니다 result=$(some_func "arg1"). 속도 차이는 MSYS에서 bash를 사용하여 더욱 두드러지게 나타납니다. 여기서 함수 호출에서 캡처하는 stdout은 거의 치명적입니다.

로컬 변수는 bash에서 동적으로 범위가 지정되므로 로컬 변수를 보내는 것이 좋습니다.

function another_func() # ARG
{
   local result
   some_func result "$1"
   echo result is $result
}

4
디버깅 / 로깅 목적으로 여러 개의 echo 문을 사용하고 싶기 때문에 도움이됩니다. 반향 캡처의 관용구는 반향을 모두 캡처하므로 실패합니다. 감사합니다!
AnneTheAgile

이것이 (두번째로 좋은) 올바른 해결책입니다! 깨끗하고 빠르며 우아하고 현명합니다.
Evi1M4chine

진짜로 유지 +2. 나는 말하려고했다. 많은 사람들 echo이 함수 내부와 명령 대체를 결합하는 것을 어떻게 무시할 수 있습니까 !
Anthony Rutledge

22

함수 출력을 캡처 할 수도 있습니다.

#!/bin/bash
function getSomeString() {
     echo "tadaa!"
}

return_var=$(getSomeString)
echo $return_var
# Alternative syntax:
return_var=`getSomeString`
echo $return_var

이상하게 보이지만 전역 변수 IMHO를 사용하는 것보다 낫습니다. 전달 매개 변수는 평소와 같이 작동하며 중괄호 또는 백틱 안에 넣습니다.


11
대체 구문 메모를 제외하고는 op가 자신의 질문에 이미 쓴 것과 똑같은 것이 아닌가?
Luca Borrione

12

가장 간단하고 강력한 솔루션은 다른 사람들이 쓴 것처럼 명령 대체를 사용하는 것입니다.

assign()
{
    local x
    x="Test"
    echo "$x"
}

x=$(assign) # This assigns string "Test" to x

단점은 별도의 프로세스가 필요하기 때문에 성능입니다.

이 주제에서 제안한 다른 기술, 즉 인수로 할당 할 변수 이름을 전달하면 부작용이 있으므로 기본 형식으로는 권장하지 않습니다. 문제는 반환 값을 계산하기 위해 함수에 일부 변수가 필요할 수 있으며 반환 값을 저장하려는 변수의 이름이 변수 중 하나를 방해 할 수 있다는 것입니다.

assign()
{
    local x
    x="Test"
    eval "$1=\$x"
}

assign y # This assigns string "Test" to y, as expected

assign x # This will NOT assign anything to x in this scope
         # because the name "x" is declared as local inside the function

물론 함수의 내부 변수를 로컬로 선언하지는 않지만 같은 이름을 가진 변수가있는 경우 실수로 부모 범위에서 관련없는 변수를 덮어 쓰는 것처럼 항상 그렇게해야합니다. .

가능한 해결 방법 중 하나는 전달 된 변수를 전역 변수로 명시 적으로 선언 한 것입니다.

assign()
{
    local x
    eval declare -g $1
    x="Test"
    eval "$1=\$x"
}

이름 "x"가 인수로 전달되면 함수 본문의 두 번째 행이 이전 로컬 선언을 겹쳐 씁니다. 그러나 이름 자체가 여전히 간섭 할 수 있으므로 리턴 값을 작성하기 전에 전달 된 변수에 이전에 저장된 값을 사용하려는 경우 맨 처음에 다른 로컬 변수에 복사해야합니다. 그렇지 않으면 결과를 예측할 수 없습니다! 게다가 이것은 최신 버전의 BASH, 즉 4.2에서만 작동합니다. 보다 이식성이 좋은 코드는 다음과 같은 효과를 갖는 명시적인 조건부 구문을 활용할 수 있습니다.

assign()
{
    if [[ $1 != x ]]; then
      local x
    fi
    x="Test"
    eval "$1=\$x"
}

아마도 가장 우아한 해결책은 함수 반환 값으로 하나의 전역 이름을 예약하고 작성한 모든 함수에서 일관되게 사용하는 것입니다.


3
이 ^^^. 캡슐화를 깨뜨리는 부주의 한 앨리어싱은 솔루션 evaldeclare -n솔루션 모두에서 큰 문제입니다 . result모든 출력 매개 변수 와 같은 단일 전용 변수 이름을 갖는 해결 방법은 충돌을 피하기 위해 호출자가 모든 것을 알기 위해 함수가 필요하지 않은 유일한 솔루션 인 것 같습니다.
Karsten

12

앞에서 언급했듯이 함수에서 문자열을 반환하는 "올바른"방법은 명령 대체입니다. 함수가 콘솔에 출력 해야하는 경우 (@Mani가 언급 한 것처럼) 함수 시작 부분에 임시 fd를 만들고 콘솔로 리디렉션하십시오. 문자열을 반환하기 전에 임시 fd를 닫으십시오.

#!/bin/bash
# file:  func_return_test.sh
returnString() {
    exec 3>&1 >/dev/tty
    local s=$1
    s=${s:="some default string"}
    echo "writing directly to console"
    exec 3>&-     
    echo "$s"
}

my_string=$(returnString "$*")
echo "my_string:  [$my_string]"

매개 변수없이 스크립트를 실행하면 ...

# ./func_return_test.sh
writing directly to console
my_string:  [some default string]

이것이 사람들을 돕는 희망

앤디


6
그것은 그것의 용도를 가지고 있지만, 전체적으로 당신은 콘솔로 명시적인 리다이렉션을하지 않아야합니다; 출력이 이미 재 지정되었거나 tty가없는 컨텍스트에서 스크립트가 실행 중일 수 있습니다. 3>&1스크립트 헤드를 복제 한 다음 함수 내에서 &1 &3다른 자리 표시자를 조작하여 문제를 해결할 수 &4있습니다. 어쨌든 모든 라운드.
jmb

8

전역 변수를 사용할 수 있습니다.

declare globalvar='some string'

string ()
{
  eval  "$1='some other string'"
} # ----------  end of function string  ----------

string globalvar

echo "'${globalvar}'"

이것은 준다

'some other string'

6

Andy의 답변에 대한 내 의견을 설명하기 위해 추가 파일 설명자 조작을 사용하여 /dev/tty다음을 사용하지 마십시오 .

#!/bin/bash

exec 3>&1

returnString() {
    exec 4>&1 >&3
    local s=$1
    s=${s:="some default string"}
    echo "writing to stdout"
    echo "writing to stderr" >&2
    exec >&4-
    echo "$s"
}

my_string=$(returnString "$*")
echo "my_string:  [$my_string]"

그래도 여전히 불쾌합니다.


3

당신이 가진 방법은 범위를 어 기지 않고 이것을 할 수있는 유일한 방법입니다. Bash에는 리턴 유형의 개념이 없으며 종료 코드 및 파일 설명자 (stdin / out / err 등) 만 있습니다.


3

주소 비키 Ronnen 다음 코드를 고려의 머리 위로 :

function use_global
{
    eval "$1='changed using a global var'"
}

function capture_output
{
    echo "always changed"
}

function test_inside_a_func
{
    local _myvar='local starting value'
    echo "3. $_myvar"

    use_global '_myvar'
    echo "4. $_myvar"

    _myvar=$( capture_output )
    echo "5. $_myvar"
}

function only_difference
{
    local _myvar='local starting value'
    echo "7. $_myvar"

    local use_global '_myvar'
    echo "8. $_myvar"

    local _myvar=$( capture_output )
    echo "9. $_myvar"
}

declare myvar='global starting value'
echo "0. $myvar"

use_global 'myvar'
echo "1. $myvar"

myvar=$( capture_output )
echo "2. $myvar"

test_inside_a_func
echo "6. $_myvar" # this was local inside the above function

only_difference



줄게

0. global starting value
1. changed using a global var
2. always changed
3. local starting value
4. changed using a global var
5. always changed
6. 
7. local starting value
8. local starting value
9. always changed

일반적인 시나리오는 test_inside_a_func함수 에 사용 된 구문을 사용하는 것이므로 대부분의 경우 두 가지 방법을 모두 사용할 수 있습니다. 출력을 캡처하는 것이 어떤 상황에서도 항상 작동하는 더 안전한 방법이므로 가능한 함수에서 반환 값을 모방 다른 언어로 Vicky Ronnen올바르게 지적하십시오.


2

옵션이 모두 열거되었습니다. 하나를 선택하면 특정 응용 프로그램에 가장 적합한 스타일의 문제가 생길 수 있으며 그 맥락에서 유용한 하나의 특정 스타일을 제공하고 싶습니다. bash에서 변수와 함수는 동일한 네임 스페이스에 없습니다. 따라서 함수의 값과 동일한 이름의 변수를 처리하는 것은 엄격하게 적용하면 이름 충돌을 최소화하고 가독성을 향상시키는 규칙입니다. 실생활의 예 :

UnGetChar=
function GetChar() {
    # assume failure
    GetChar=
    # if someone previously "ungot" a char
    if ! [ -z "$UnGetChar" ]; then
        GetChar="$UnGetChar"
        UnGetChar=
        return 0               # success
    # else, if not at EOF
    elif IFS= read -N1 GetChar ; then
        return 0           # success
    else
        return 1           # EOF
    fi
}

function UnGetChar(){
    UnGetChar="$1"
}

이러한 기능을 사용하는 예는 다음과 같습니다.

function GetToken() {
    # assume failure
    GetToken=
    # if at end of file
    if ! GetChar; then
        return 1              # EOF
    # if start of comment
    elif [[ "$GetChar" == "#" ]]; then
        while [[ "$GetChar" != $'\n' ]]; do
            GetToken+="$GetChar"
            GetChar
        done
        UnGetChar "$GetChar"
    # if start of quoted string
    elif [ "$GetChar" == '"' ]; then
# ... et cetera

보시다시피, 반품 상태는 필요할 때 사용할 수 있으며 필요하지 않은 경우 무시합니다. "returned"변수도 마찬가지로 사용하거나 무시할 수 있지만 물론 함수가 호출 된 후에 만 가능 합니다.

물론 이것은 컨벤션 일뿐입니다. 반환하기 전에 관련 값을 자유롭게 설정하지 못하거나 (따라서 함수 시작시 항상 null을 사용하는 규칙) 함수를 다시 호출하여 간접적으로 값을 짓밟을 수 있습니다. 여전히 bash 함수를 많이 사용하는 경우 매우 유용합니다.

이것이 "펄로 이동해야한다"라는 표식이라는 정서와는 반대로, 나의 철학은 어떤 언어의 복잡성을 관리하기 위해 관습이 항상 중요하다는 철학이다.


2

echo문자열을 사용할 수 있지만 |함수를 다른 것으로 파이프 ( )하여 잡을 수 있습니다 .

ShellCheck 는이 사용을 더 이상 사용되지 않음으로보고 expr하지만을 사용 하여 수행 할 수 있습니다 .


문제는 파이프 오른쪽에있는 것이 서브 쉘이라는 것입니다. 따라서 myfunc | read OUTPUT ; echo $OUTPUT아무것도 산출하지 않습니다. myfunc | ( read OUTPUT; echo $OUTPUT )기대 값을 얻고 오른쪽에 무슨 일이 일어나고 있는지 명확하게합니다. 그러나 물론 필요한 경우 OUTPUT을 사용할 수 없습니다.
Ed Randall

2

그들은 호출자가 (사용 여부를 변수 이름으로 전달할 수있는 '라는 이름 출력 변수'계획의 핵심 문제 eval또는 것은 declare -n) 실수로 앨리어싱, 즉 이름 충돌입니다 :보기의 캡슐화 점에서를이를 추가하거나 이름을 바꿀 수 없습니다하는 끔찍한 함수의 모든 호출자를 먼저 확인하지 않고 함수의 로컬 변수 로, 출력 매개 변수와 동일한 이름을 전달하지 않으려는지 확인하십시오. (또는 다른 방향으로, 사용하려는 출력 매개 변수가 해당 함수의 로컬이 아닌지 확인하기 위해 호출하는 함수의 소스를 읽을 필요가 없습니다.)

그 주위의 유일한 방법은 REPLY( Evi1M4chine에서 제안한 ) 단일 전용 출력 변수 또는 Ron Burk에서 제안한 것과 같은 규칙을 사용하는 것입니다. 입니다.

그러나 함수가 고정 출력 변수를 사용하도록 할 수 있습니다 내부적 다음 상단에 설탕을 추가 하여 호출자 에서이 사실숨길 수call 있습니다. 다음 예제 의 함수로 수행 한 것과 같습니다. 이것을 개념 증명이라고 생각하지만 요점은

  • 이 함수는 항상 반환 값을에 할당하고 REPLY평소와 같이 종료 코드를 반환 할 수도 있습니다
  • 호출자의 관점에서 반환 값은 다음을 포함하여 모든 변수 (로컬 또는 전역)에 할당 될 수 있습니다. REPLY ( wrapper예 참조 ). 함수의 종료 코드가 전달되므로 예를 들어 하나 if이상의 while구성 에서이를 사용하면 예상대로 작동합니다.
  • 구문 적으로 함수 호출은 여전히 ​​단순한 단일 명령문입니다.

이것이 작동하는 이유는 call함수 자체에 지역 변수가 없으며 REPLY이름 충돌 가능성을 피하면서 이외의 변수를 사용하지 않기 때문 입니다. 호출자 정의 출력 변수 이름이 지정되는 시점에서, 우리는 호출되는 call함수 의 범위가 아니라 호출자의 범위 (기술적으로 동일한 함수 범위)에 있습니다.

#!/bin/bash
function call() { # var=func [args ...]
  REPLY=; "${1#*=}" "${@:2}"; eval "${1%%=*}=\$REPLY; return $?"
}

function greet() {
  case "$1" in
    us) REPLY="hello";;
    nz) REPLY="kia ora";;
    *) return 123;;
  esac
}

function wrapper() {
  call REPLY=greet "$@"
}

function main() {
  local a b c d
  call a=greet us
  echo "a='$a' ($?)"
  call b=greet nz
  echo "b='$b' ($?)"
  call c=greet de
  echo "c='$c' ($?)"
  call d=wrapper us
  echo "d='$d' ($?)"
}
main

산출:

a='hello' (0)
b='kia ora' (0)
c='' (123)
d='hello' (0)

1

스칼라배열 값 객체를 모두 반환하는 bash 패턴 :

정의

url_parse() { # parse 'url' into: 'url_host', 'url_port', ...
   local "$@" # inject caller 'url' argument in local scope
   local url_host="..." url_path="..." # calculate 'url_*' components
   declare -p ${!url_*} # return only 'url_*' object fields to the caller
}

기도

main() { # invoke url parser and inject 'url_*' results in local scope
   eval "$(url_parse url=http://host/path)" # parse 'url'
   echo "host=$url_host path=$url_path" # use 'url_*' components
}

0

내 프로그램에서 관례에 따르면 이것은 기존 $REPLY변수의 read용도이며 정확한 목적으로 사용됩니다.

function getSomeString {
  REPLY="tadaa"
}

getSomeString
echo $REPLY

echoes

tadaa

그러나 피할 충돌에, 다른 글로벌 변수는 할 것입니다.

declare result

function getSomeString {
  result="tadaa"
}

getSomeString
echo $result

충분하지 않으면 Markarian451 의 솔루션을 권장 합니다.


-2
agt@agtsoft:~/temp$ cat ./fc 
#!/bin/sh

fcall='function fcall { local res p=$1; shift; fname $*; eval "$p=$res"; }; fcall'

function f1 {
    res=$[($1+$2)*2];
}

function f2 {
    local a;
    eval ${fcall//fname/f1} a 2 3;
    echo f2:$a;
}

a=3;
f2;
echo after:a=$a, res=$res

agt@agtsoft:~/temp$ ./fc
f2:10
after:a=3, res=
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.