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 \\\$\\$\$$l3→ 53294정확히 모듈식이 아닙니다.
— Profpatsch

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

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

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

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


6

의 값이 FOO유효한 변수 이름 (예 :) BAR이고 eval \$$FOO값 BAR을 별도의 단어로 나누고 각 단어를 와일드 카드 패턴으로 취급하며 결과의 첫 번째 단어를 명령으로 실행하고 다른 단어를 인수로 전달합니다. 달러 앞의 백 슬래시는 문자 그대로 처리되므로 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당신이 생각하는 것보다 훨씬 자주 설치되어 있다고 생각합니다. 이 링크 sh는 bash자주 연결 되지는 않지만 항상 그런 것은 아니지만 쉘 스크립트에서 계속 사용할 수 있습니다. 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.