
환경을 수정하는 명령의 출력을 변수로 저장하는 방법은 무엇입니까?
나는 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를 설정하지 않으면 작동하지 않습니다. 하지만 다른 방법은 다음과 같이 보일 수 있습니다.name
var
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
$fout
for
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'