bash 및 zsh에서 이중 및 삼중 대체


23

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

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

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

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

답변:


15

\확대 방지하기 위해 사용되어야한다 $$(현재의 프로세스 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

그러나 l3=l2; eval eval eval echo \\\$\\$\$$l353294정확히 모듈식이 아닙니다.
Profpatsch

2
왜 내가 뭔가를 할 수 eval $(eval echo \$$l2)없습니까? 나는 bash를 싫어한다.
Profpatsch

@Profpatsch : 더 많은 예제가 추가되었습니다.
choroba

아, 그것은 n²입니다. 나는 이것이 왜 그런지 알고 싶지도 않습니다.
Profpatsch

2
@Profpatsch : 쉬워요. 각 단계에서 백 슬래시 수는 반으로 줄어 듭니다 (실제로 2ⁿ).
choroba


6

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

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

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

eval "value=\${$FOO}"

1
첫 단어가 명령으로 사용되지 않도록 변수로 설정? 이런 논리는 이해하기 어렵습니다. bash와 관련하여 일반적인 문제입니다.
Profpatsch

4

{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?}\""
}

나를 위해 작동하는 크로스 셸 솔루션. "POSIX 호환 이중 변수 확장"이 향후이 답변으로 리디렉션되기를 바랍니다.
BlueDrink9

2

마찬가지로이 ${$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


고마워요. 이것이 zsh 전용이기 때문에 부끄러운 일이므로 실제로 스크립트에 넣을 수는 없습니다 (모두 bash가 있지만 다른 방법은 아닙니다).
Profpatsch

나는 zsh당신이 생각하는 것보다 훨씬 자주 설치되어 있다고 생각합니다. 이 링크 shbash자주 연결 되지는 않지만 항상 그런 것은 아니지만 쉘 스크립트에서 계속 사용할 수 있습니다. Perl이나 Python처럼 생각하십시오.
chepner

2

왜 그렇게해야합니까?

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

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

허, 몇 단계를 사용하는 것은 지금까지 나에게 일어나지 않았다. .. 이상하다.
Profpatsch

내 관점에서 @Profpatsch가 왜 그렇게하고 싶어하는지는 분명 합니다 . 가장 직관적 인 방법이기 때문입니다. bash에서 여러 대체를 구현 하기어렵다는 것은 또 다른 것입니다.
Nikos Alexandris

2

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?}\""
}

이 답변을 이전 답변과 결합하지 않은 이유가 있습니까? 한눈에 나는 그들 사이의 차이점을 보지 못합니다.
BlueDrink9
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.