[unix] bash 및 zsh에서 이중 및 삼중 대체

이 질문 의 배경 부분에 대한 후속 조치 .

에서 이중 치환에 bash사용할 수 있습니다 . 둘 다, 구식 (hack-y)이 작동합니다.${!FOO}zsh ${(P)FOO}eval \$$FOO

따라서 나에게 가장 똑똑하고 가장 논리적 인 것은 ${${FOO}}, ${${${FOO}}}…이중 / 삼중 / n 대체입니다. 왜 이것이 예상대로 작동하지 않습니까?

두 번째로 다음은 무엇 않습니다 \에서 할 eval문? 나는 그것이 탈출이라고 생각하여 eval \$$$FOO불가능한 것을 만듭니다 . 모든 쉘에서 작동하는 트리플 / n 대체 방법?



답변

\확대 방지하기 위해 사용되어야한다 $$(현재의 프로세스 ID). 삼중 치환의 경우 이중 평가가 필요하므로 각 평가에서 원하지 않는 확장을 피하기 위해 더 많은 이스케이프가 필요합니다.

#! /bin/bash
l0=value
l1=l0
l2=l1
l3=l2
l4=l3

echo $l0
eval echo \$$l1
eval eval echo \\$\$$l2
eval eval eval echo \\\\$\\$\$$l3
eval eval eval eval echo \\\\\\\\$\\\\$\\$\$$l4


답변

#!/bin/bash

hello=world
echo=hello

echo $echo ${!echo}


답변

의 값이 FOO유효한 변수 이름 (예 🙂 BAR이고 eval \$$FOOBAR을 별도의 단어로 나누고 각 단어를 와일드 카드 패턴으로 취급하며 결과의 첫 번째 단어를 명령으로 실행하고 다른 단어를 인수로 전달합니다. 달러 앞의 백 슬래시는 문자 그대로 처리되므로 eval내장 문자에 전달되는 인수 는 4 자 문자열 $BAR입니다.

${${FOO}}구문 오류입니다. 일반적인 쉘에는 이러한 기능이 없기 때문에 “이중 대체”를 수행하지 않습니다 (어쨌든이 구문을 사용하지 않음). zsh을에서 ${${FOO}} 이다 유효하고 이중 대체이지만, 그것은 당신이 원하는 것과 다르게 동작 : 그것의 가치에 두 개의 연속 변환을 수행 FOO, 신원 변환되어 둘을, 그래서 글을 쓰는 단지 멋진 방법입니다 ${FOO}.

변수의 값을 변수로 취급하려면 올바르게 인용하도록주의하십시오. 결과를 변수로 설정하면 훨씬 쉽습니다.

eval "value=\${$FOO}"


답변

{ba,z}sh 해결책

다음은 둘 다에서 작동하는 함수입니다 {ba,z}sh. POSIX 와도 호환됩니다.

인용에 화를 내지 않고 다음과 같이 여러 수준의 간접적으로 사용할 수 있습니다.

$ a=b
$ b=c
$ c=d
$ echo $(var_expand $(var_expand $a)
d

또는 더 많은 (!?!) 간접 레벨이있는 ​​경우 루프를 사용할 수 있습니다.

주어진 경우 경고합니다 :

  • 널 입력
  • 설정되지 않은 변수 이름
# Expand the variable named by $1 into its value. Works in both {ba,z}sh
# eg: a=HOME $(var_expand $a) == /home/me
var_expand() {
  if [ -z "${1-}" ] || [ $# -ne 1 ]; then
    printf 'var_expand: expected one argument\n' >&2;
    return 1;
  fi
  eval printf '%s' "\"\${$1?}\""
}


답변

마찬가지로이 ${$foo}의 장소에서 작동하지 않습니다 ${(P)foo}zsh, 어느 쪽도 않습니다 ${${$foo}}. 각 간접 레벨을 지정하면됩니다.

$ foo=bar
$ bar=baz
$ baz=3
$ echo $foo
bar
$ echo ${(P)foo}
baz
$ echo ${(P)${(P)foo}}
3

물론 중첩 대체를 허용하지 않기 때문에 ${!${!foo}}에서 작동 하지 않습니다.bashbash


답변

왜 그렇게해야합니까?

항상 다음과 같은 여러 단계로 수행 할 수 있습니다.

eval "l1=\${$var}"
eval "l2=\${$l1}"
...

또는 다음과 같은 기능을 사용하십시오.

deref() {
  if [ "$1" -le 0 ]; then
    eval "$3=\$2"
  else
    eval "deref $(($1 - 1)) \"\${$2}\" \"\$3\""
  fi
}

그때:

$ a=b b=c c=d d=e e=blah
$ deref 3 a res; echo "$res"
d
$ deref 5 a res; echo "$res"
blah

FWIW zsh:

$ echo ${(P)${(P)${(P)${(P)a}}}}
blah


답변

POSIX 솔루션

인용에 화를 내지 않고이 기능을 다음과 같이 여러 수준의 간접적으로 사용할 수 있습니다.

$ a=b
$ b=c
$ c=d
$ echo $(var_expand $(var_expand $a)
d

또는 더 많은 (!?!) 간접 레벨이있는 ​​경우 루프를 사용할 수 있습니다.

주어진 경우 경고합니다 :

  • 널 입력
  • 하나 이상의 논쟁
  • 설정되지 않은 변수 이름
# Expand the variable named by $1 into its value. Works in both {ba,z}sh
# eg: a=HOME $(var_expand $a) == /home/me
function var_expand {
  if [ "$#" -ne 1 ] || [ -z "${1-}" ]; then
    printf 'var_expand: expected one argument\n' >&2;
    return 1;
  fi
  eval printf '%s' "\"\${$1?}\""
}