환경을 수정하는 명령의 출력을 변수로 저장

환경을 수정하는 명령의 출력을 변수로 저장

환경을 수정하는 명령의 출력을 변수로 저장하는 방법은 무엇입니까?

나는 bash 쉘을 사용하고 있습니다.

내가 다음을 가지고 있다고 가정합니다:

function f () { a=3; b=4 ; echo "`date`: $a $b"; }

이제 명령을 사용하여 다음을 실행할 수 있습니다 f.

$ a=0; b=0; f; echo $a; echo $b; echo $c
Sat Jun 28 21:27:08 CEST 2014: 3 4
3
4

f하지만 의 출력을 변수에 저장하고 싶어서 c다음을 시도했습니다.

a=0; b=0; c=""; c=$(f); echo $a; echo $b; echo $c

하지만 불행하게도 나는 다음과 같은 결과를 얻었습니다.

0
0
Sat Jun 28 21:28:03 CEST 2014: 3 4

그래서 여기에는 환경 변화가 없습니다.

함수뿐만 아니라 명령의 출력을 변수에 저장하고 환경 변경 사항을 저장하는 방법은 무엇입니까?

새 하위 쉘이 열리는 것이 문제라는 것을 알고 있지만 $(...)몇 가지 해결 방법이 가능합니까?

답변1

Bash 4 이상을 사용하는 경우 다음을 사용할 수 있습니다.공동 프로세스:

function f () { a=3; b=4 ; echo "`date`: $a $b"; }
coproc cat
f >&${COPROC[1]}
exec {COPROC[1]}>&-
read c <&${COPROC[0]}
echo a $a
echo b $b
echo c $c

출력됩니다

a 3
b 4
c Sun Jun 29 10:08:15 NZST 2014: 3 4

coproc주어진 명령(여기서는 cat)을 실행하는 새 프로세스를 생성합니다. PID를 배열에 저장 COPROC_PID하고 표준 출력/입력 파일 설명자를 배열에 저장 합니다 COPROC(예:pipe(2), 또는 참조여기또는여기).

여기서는 coprocess running 을 가리키는 표준 출력으로 함수를 실행합니다 cat.read그것에서. cat입력을 다시 내뱉기 때문에 함수의 출력을 변수로 가져옵니다. 영원히 기다리지 않도록 exec {COPROC[1]}>&-파일 설명자를 닫습니다 .cat


read한 번에 한 줄만 사용됩니다 . 줄 배열을 가져오는 데 사용할 수도 mapfile있고, 원하는 대로 파일 설명자를 사용할 수도 있습니다.

exec {COPROC[1]}>&-현재 버전의 Bash에서 작동하지만 이전 4 시리즈 버전에서는 먼저 파일 설명자를 간단한 변수에 저장해야 합니다 fd=${COPROC[1]}; exec {fd}>&-. 변수가 설정되지 않은 경우 표준 출력이 닫힙니다.


3 시리즈 버전의 Bash를 사용하는 경우 다음과 같은 효과를 얻을 수 있습니다.mkfifo, 그러나 그 시점에서 실제 파일을 사용하는 것보다 훨씬 낫지는 않습니다.

답변2

f이미 호출자와 동일한 셸에서 실행되어 및 같은 변수를 수정할 수 있다고 a믿고 있다면 b함수도 설정해 두는 것이 어떨까요 c? 다시 말해서:

$ function f () { a=3; b=4 ; c=$(echo "$(date): $a $b"); }
$ a=0; b=0; f; echo $a; echo $b; echo $c
3
4
Mon Jun 30 14:32:00 EDT 2014: 3 4

한 가지 가능한 이유는 출력 변수가 다른 위치에서 호출될 때 다른 이름(c1, c2 등)을 가져야 한다는 것일 수 있지만, 함수를 c_TEMP로 설정하고 호출자가 그렇게 하도록 함으로써 이 문제를 해결할 수 있습니다 c1=$c_TEMP.

답변3

복잡하지만 시도해 보세요

f > c.file
c=$(cat c.file)

(및 선택적으로 rm c.file).

답변4

모든 변수를 할당하고 동시에 출력을 작성하면 됩니다.

f() { c= ; echo "${c:=$(date): $((a=3)) $((b=4))}" ; }

이제 그렇게 하면:

f ; echo "$a $b $c"

귀하의 출력은 다음과 같습니다

Tue Jul  1 04:58:17 PDT 2014: 3 4
3 4 Tue Jul  1 04:58:17 PDT 2014: 3 4

이는 완전히 POSIX 이식 가능한 코드입니다. 저는 처음 c='' 매개변수 확장이 동시 변수 할당 + 평가를 숫자로만 제한하기 때문에 처음에는 널 문자열(좋다$((var=num)) )또는 null 또는 존재하지 않는 값에서 - 즉, 동시에 설정할 수 없습니다.그리고해당 변수에 이미 값이 할당된 경우 변수를 임의의 문자열로 평가합니다. 그래서 시도하기 전에 비어 있는지 확인합니다. c할당을 시도하기 전에 비우지 않으면 확장은 이전 값만 반환합니다.

단지 보여주기 위해:

sh -c '
    c=oldval a=1
    echo ${c:=newval} $((a=a+a))
'
###OUTPUT###
oldval 2

newval~이다~ 아니다인라인 산술 할당은 항상 발생하는 반면 는 으로 확장되므로 $c인라인 에 할당됩니다 . 하지만 만약oldval${word}$((=))$c 없다 oldval비어 있거나 설정되지 않았습니다.

sh -c '
    c=oldval a=1
    echo ${c:=newval} $((a=a+a)) 
    c= a=$((a+a))
    echo ${c:=newval} $((a=a+a))
'
###OUTPUT###
oldval 2
newval 8

...then은 newval즉시 에 할당되고 확장됩니다 $c.

이를 수행하는 다른 모든 방법에는 일종의 2차 평가가 포함됩니다. 예를 들어, 한 지점과 다른 지점에서 f()명명된 변수 에 의 출력을 할당하고 싶다고 가정해 보겠습니다 . 현재 작성된 대로 호출자의 범위에 var를 설정하지 않으면 작동하지 않습니다. 하지만 다른 방법은 다음과 같이 보일 수 있습니다.namevar

f(){ fout_name= fout= set -- "${1##[0-9]*}" "${1%%*[![:alnum:]]*}"
    (: ${2:?invalid or unspecified param - name set to fout}) || set --
    export "${fout_name:=${1:-fout}}=${fout:=$(date): $((a=${a:-50}+1)) $((b=${b:-100}-4))}"
    printf %s\\n "$fout"
}

f &&
    printf %s\\n \
        "$fout_name" \
        "$fout" \
        "$a" "$b"

아래에 더 나은 형식의 예를 제공했지만 위와 같이 호출하면 출력은 다음과 같습니다.

sh: line 2: 2: invalid or unspecified param - name set to fout
Wed Jul  2 02:27:07 PDT 2014: 51 96
fout
Wed Jul  2 02:27:07 PDT 2014: 51 96
51
96

또는 다른 $ENV인수를 사용하여:

b=9 f myvar &&
    printf %s\\n \
        "$fout_name" \
        "$fout" \
        "$myvar" \
        "$a" "$b"

###OUTPUT###

Tue Jul  1 19:56:42 PDT 2014: 52 5
myvar
Tue Jul  1 19:56:42 PDT 2014: 52 5
Tue Jul  1 19:56:42 PDT 2014: 52 5
52
5

두 번 평가할 때 가장 어려운 점은 변수가 따옴표를 깨지 않고 임의의 코드를 실행하지 않도록 하는 것입니다. 변수가 더 많이 평가될수록 더 어려워집니다. 여기서 매개변수 확장은 많은 도움이 되며, export반대로 사용하는 것이 eval훨씬 더 안전합니다.

위의 예에서는 f()먼저 null 문자열을 할당 $fout''다음 유효한 변수 이름을 테스트하기 위해 위치 매개변수를 설정합니다. 두 테스트가 모두 통과하지 못하면 에 메시지가 내보내지고 stderr의 기본값이 fout에 할당됩니다 $fout_name. 그러나 테스트에 관계없이 항상 사용자가 지정한 이름 $fout_name중 하나에 할당되며 , 선택적으로 지정한 이름에는 항상 함수의 출력 값이 할당됩니다. 이를 보여주기 위해 다음과 같은 작은 루프를 작성했습니다 .fout$foutfor

for v in var '' "wr;\' ong"
    do sleep 10 &&
        a=${a:+$((a*2))} f "$v" || break
    echo "${v:-'' #null}" 
    printf '#\t"$%s" = '"'%s'\n" \
        a "$a" b "$b" \
        fout_name "$fout_name" \
        fout "$fout" \
        '(eval '\''echo "$'\''"$fout_name"\")' \
            "$(eval 'echo "$'"$fout_name"\")"
 done

변수 이름과 매개변수 확장을 사용하여 일부를 재생합니다. 질문이 있으면 물어보세요. 저것오직여기에 이미 표시된 함수에서 동일한 몇 줄을 실행합니다. 적어도 $a$b변수는 호출 시 정의되었는지 또는 이미 설정되었는지에 따라 다르게 동작한다는 점은 언급할 가치가 있습니다. 그럼에도 불구하고 는 for데이터 세트의 형식을 지정하고 에서 제공하는 것 외에는 거의 아무것도 수행하지 않습니다 f(). 보세요:

###OUTPUT###

Wed Jul  2 02:50:17 PDT 2014: 51 96
var
#       "$a" = '51'
#       "$b" = '96'
#       "$fout_name" = 'var'
#       "$fout" = 'Wed Jul  2 02:50:17 PDT 2014: 51 96'
#       "$(eval 'echo "$'"$fout_name"\")" = 'Wed Jul  2 02:50:17 PDT 2014: 51 96'
sh: line 2: 2: invalid or unspecified param - name set to fout
Wed Jul  2 02:50:27 PDT 2014: 103 92
'' #null
#       "$a" = '103'
#       "$b" = '92'
#       "$fout_name" = 'fout'
#       "$fout" = 'Wed Jul  2 02:50:27 PDT 2014: 103 92'
#       "$(eval 'echo "$'"$fout_name"\")" = 'Wed Jul  2 02:50:27 PDT 2014: 103 92'
sh: line 2: 2: invalid or unspecified param - name set to fout
Wed Jul  2 02:50:37 PDT 2014: 207 88
wr;\' ong
#       "$a" = '207'
#       "$b" = '88'
#       "$fout_name" = 'fout'
#       "$fout" = 'Wed Jul  2 02:50:37 PDT 2014: 207 88'
#       "$(eval 'echo "$'"$fout_name"\")" = 'Wed Jul  2 02:50:37 PDT 2014: 207 88'

관련 정보