bash의 함수 참조를 통해 stdout을 캡처하고 변수를 수정해야 합니다.

bash의 함수 참조를 통해 stdout을 캡처하고 변수를 수정해야 합니다.

(하위 쉘의) 함수 호출에서 변수로 stdout을 쉽게 캡처할 수 있습니다.

val="$(get_value)"

셸에서 변수(예: 배열)를 수정할 수도 있습니다.인용하다동일한 셸에서 다음과 같이 가정해 보겠습니다.

function array.delete_by_index {
    local array_name="$1"
    local key="$2"

    unset "$array_name[$key]"
    eval "$array_name=(\"\${$array_name[@]}\")"
}

array.delete_by_index "array1" 0

하지만 나는 두 가지를 모두 깔끔한 방법으로 수행하는 방법을 찾으려고 노력하고 있습니다. 내가 원하는 예는 배열에서 값을 팝하는 것입니다.

function array.pop {
    local array_name="$1"

    local last_index=$(( $(eval "echo \${#$array_name[@]}") - 1 ))
    local tmp="$array_name[\"$last_index\"]"
    echo "${!tmp}"

    # Changes "$array_name" here, but not in caller since this is a sub shell
    array.delete_by_index "$array_name" $last_index
}

val="$(array.pop "array1")"

stdout을 변수로 캡처하는 모든 형태에는 bash의 하위 쉘이 필요하며 하위 쉘을 사용하면 호출자의 컨텍스트에서 참조로 값을 변경할 수 없는 것 같습니다.

이것을 달성하기 위한 마법의 배시즘을 아는 사람이 있는지 궁금합니다. 나는 특히 파일 시스템에서 어떤 종류의 파일/fifo를 사용하는 솔루션을 원하지 않습니다.

두 번째 답변이 질문에는이 구성은 분명히 출력 캡처를 허용하지만 하위 쉘을 사용하는 것은 허용하지 않기 때문에 를 ksh사용하면 이것이 가능하다는 것을 나타내는 것 같습니다 . val="${ cmd; }"예, 기술적으로는 ksh로 전환할 수 있지만 bash에서 이것이 가능한지 궁금합니다.

답변1

이것은 bash(버전 4.3부터) 작동하며 ksh93"bashify"하려면 typeset전역 범위의 모든 기능을 (모든 옵션을 유지하면서!)로 바꾸십시오. 솔직히 말해서 Bash가 왜 그런 것에만 관심이 있는지 모르겠습니다.localtypesetdeclaretypeset

function stack_push
{
    typeset -n _stack="$1"
    typeset element="$2"

    _stack+=("$element")
}

function stack_pop
{
    typeset -n _stack="$1"
    typeset -n _retvar="$2"

    _retvar="${_stack[-1]}"

    unset _stack[-1]
}

typeset -a stack=()

stack_push stack "hello"
stack_push stack "world"

stack_pop stack value
printf '%s ' "$value"

stack_pop stack value
printf '%s\n' "$value"

이는 함수에서 nameref를 사용하면 피할 수 있습니다 eval(저는 eval어떤 스크립트에서도 이것을 사용한 적이 없습니다!). 함수에 팝된 값을 저장할 장소를 제공 하면 stack_pop서브셸 사용을 피할 수 있습니다 . 서브쉘을 피함으로써 함수는 stack_pop외부 범위의 변수 값을 수정할 수 있습니다.stack

함수에서 지역 변수의 밑줄은 nameref가 참조하는 변수와 동일한 이름을 갖는 것을 방지하는 것입니다(Bash는 이를 좋아하지 않습니다. ksh신경 쓰지 않습니다.이 문제).

ksh다음과 같이 함수를 작성할 수 있습니다 .stack_pop

function stack_pop
{
    typeset -n _stack="$1"

    printf '%s' "${_stack[-1]}"

    unset _stack[-1]
}

그리고 전화해

printf '%s %s\n' "${ stack_pop stack }" "${ stack_pop stack }"

( ${ ... }와 동일 $( ... )하지만 서브쉘을 생성하지 않음)

하지만 나는 이것을 별로 좋아하지 않는다. IMHO 데이터를 stdout으로 보낼 필요가 없으며 데이터를 얻기 stack_pop위해 호출할 필요도 없습니다 . ${ ... }나는 원래의 것을 선호 하고 필요할 경우 위의 작업을 수행하는 stack_pop것을 추가 할 수 있습니다.stack_pop_print

stack_popBash의 경우 게시물 시작 부분에서 를 사용한 다음 스택의 최상위 요소를 삭제하지 않고 stdout에 인쇄하는 을 사용할 수 있습니다 ( 서브셸에서 실행될 stack_top_print가능성이 높으므로 삭제할 수 없음 ).$( ... )

답변2

파일을 사용하지 않거나 함수를 수정하지 않는 것에 대해 제가 생각할 수 있는 유일한 해결책은 명령을 두 번 실행하는 것입니다. 하나는 하위 쉘에서 캡처하고 다른 하나는 상위 쉘에서 변수를 수정합니다.

val="$(array.pop "array1")"
배열.팝 배열1

함수를 재정의할 수 있는 옵션이 있는 경우:

함수 array.pop {
    로컬 배열 이름 = "$1";
    Local last_index=$(( $(eval "echo \${#$array_name[@]}") - 1 ));
    로컬 tmp="$array_name[\"$last_index\"]";
    에코 "${!tmp}";

    만약 [[ $2 ]];
        eval "$2=\"${!tmp}\"" # 여기에 마법 공격이 있습니다.
    필리핀 제도

    array.delete_by_index "$array_name" "$last_index";
}

다음으로 실행하면 array.pop 'array1' variablename변수가 존재하지 않더라도 변수가 초기화됩니다.

$array1=(하나 둘 셋)
$array.pop array1 var
$echo "$var"
$ echo "${array1[*]}"
하나 또는 둘

관련 정보