eval echo에서 사용할 수 있도록 xargs에 파이프 값을 인수로 전달합니다.

eval echo에서 사용할 수 있도록 xargs에 파이프 값을 인수로 전달합니다.

템플릿으로 사용하는 텍스트 파일이 있는데 다음과 같습니다.

Hostname     : $HOSTNAME
Host Address : $HOSTADDRESS

내 bash 스크립트는 두 개의 변수를 설정 HOSTNAME하고 HOSTADDRESS템플릿 파일을 읽은 다음 을 실행하여 and 를 eval확장합니다 .$HOSTNAME$HOSTADDRESS

HOSTNAME="SH_SQL_0089"
HOSTADDRESS="172.16.3.44"
TEMPLATE=`cat template.txt`
MESSAGE=`eval echo $TEMPLATE`

결과 값은 다음과 같습니다 MESSAGE.

Hostname     : SH_SQL_0089
Host Address : 172.16.3.44

마지막 두 줄을 다음과 같이 단순화할 수 있습니까?

MESSAGE=$(cat template.txt | eval echo ????)

나는 다음을 사용하려고합니다 xargs:

MESSAGE=$( cat template.txt | xargs -i bash -c "eval echo {}" )

$HOSTNAME하지만 캐리지 리턴을 교체하고 제거할 뿐입니다 .

이 작업을 수행하려는 이유는 세 번째 처리 단계를 추가하고 eval출력을 해당 단계로 파이프해야 하기 때문입니다. 나는 매우 가까워진 것 같은 느낌이 든다.

답변1

우선, 대체가 하위 프로세스(시작된 프로세스 ) xargs에서 수행되기 때문에 여기서는 작동하지 않습니다 . 그러나 쉘 변수가 아닌 환경 변수만 하위 프로세스에 전달됩니다. 분명히 스크립트가 시작될 때 스크립트 환경에 이미 존재하지만 그렇지 않습니다. 이 시점에서 모든 변수를 내보내고 싶은 유혹을 받을 수도 있지만, 그것조차도 많은 인용 문제에 직면하기 때문에 좋은 해결책이 아닙니다. 템플릿에 공백이 포함되어 있거나 공백을 보존하려는 경우 문제가 발생합니다.bashxargsHOSTNAMEHOSTADDRESSxargs\"'

이제 현재 코드를 살펴보세요. 참조 문제를 제외하면 MESSAGE=`eval echo $TEMPLATE`코드를 작성하는 방법이 복잡합니다. eval MESSAGE=$TEMPLATE그리고 인용 문제가 있습니다. 예를 들어 모든 공백이 축소되어 있음을 알 수 있습니다. 당신은 들었다바비 테이블, 그렇지 않나요? 셸 확장 규칙은 복잡하지만 정신을 차리기 위한 몇 가지 규칙이 있습니다.

  • "$foo"변수 대체와 명령 대체는 항상 큰따옴표로 묶입니다 "$(bar)". 따옴표를 생략해야 하는 이유를 이해한다면 이 규칙을 위반할 수 있습니다.
  • $(…)대신 `…`명령 대체에 사용하십시오 . 백틱 형식 내에서 인용하는 것은 모호하고 이식성이 없지만 내부적으로 인용하는 것은 $(…)일반적입니다.
  • eval총구 아래를 누를 때만 사용됩니다. 이 작업을 수행하는 경우 주의를 기울여 최대한 단순하게 유지하십시오.

그러면 무엇이 잘못될 수 있습니까 eval MESSAGE="$TEMPLATE"? 쉘이 평가할 수 있게 해줍니다.

MESSAGE=Hostname     : $HOSTNAME
Host Address : $HOSTADDRESS

이런, 값이 있어야 할 부분에 따옴표를 넣어야 합니다 MESSAGE. 따옴표에 도달해야 하므로 eval문자 그대로 셸 확장의 첫 번째 단계인 를 거쳐야 합니다 eval MESSAGE="\"$TEMPLATE\"".

MESSAGE="Hostname     : $HOSTNAME
Host Address : $HOSTADDRESS"

더 좋지만 이제 Bobby Tables 주입 패턴이 생겼습니다. 템플릿에 참조가 포함되어 있으면 어떻게 될까요? 그런 다음 따옴표를 이스케이프 처리해야 합니다. 큰따옴표 사이의 네 문자는 특별한 의미를 가지며 그 의미를 유지 \"$`하려면 $다른 세 문자 앞에 백슬래시를 추가하십시오.

TEMPLATE=$(sed -e 's/[\\"`]/\\&/g' <template.txt)
eval MESSAGE="\"$TEMPLATE\""

이제 쉘이 평가할 것입니다

MESSAGE="Hostname     : $HOSTNAME
Host Address : $HOSTADDRESS
Name         : Bobby \"drop\" O'Tables"

모두 괜찮습니다.

여기서 올바른 인용은 예상치 못한 구문 분석 문제를 방지하기 위한 것입니다. 템플릿을 제어하는 ​​사람은 여전히 ​​셸 액세스(사용법 $(hello))를 갖습니다.

쉘 스크립트에 템플릿을 포함시키려면 자연스럽게 다음을 사용할 수 있습니다.트레독.

MESSAGE=$(cat <<EOF)
Hostname     : $HOSTNAME
Host Address : $HOSTADDRESS
EOF

그러나 외부 템플릿을 사용하려면 2단계 평가를 수행해야 하므로 eval내부 heredocs와 같은 펑키한 작업에 대해서는 bash의 구문 분석을 사용하는 것이 매우 문제가 됩니다 eval. 적어도 bash 4를 사용하면 확실히 할 수 있는 방법이 있지만 기회를 잡는 것은 권장하지 않습니다.

답변2

쉘이 변수 대체를 적용하기를 원합니다. 즉, 템플릿 텍스트는 쉘 자체에서 명령의 일부로 읽어야 하지만 템플릿도 여러 줄 텍스트이므로 일반적으로 행 기반 처리에 들어가는 것이 약간 까다롭습니다. UNIX 명령으로 수행됩니다.

한 가지 방법은 bash를 사용하는 것입니다.여기 문서(쉘 프롬프트에 입력된 모든 명령이 표시됩니다):

  1. 템플릿 파일 만들기:

    $ cat template.txt
    Hi, $NAME
    
    Welcome to $PLACE
    
  2. 출구템플릿 텍스트에서 대체되어야 하는 변수:

    $ export NAME=Bob
    $ export PLACE='unix&linux'
    
  3. bash에서 단일 "줄 바꿈" \n 문자를 포함하는 쉘 변수를 만듭니다. 가장 쉬운 방법은 문자열을 열고 줄 바꿈 문자를 입력한 후 닫는 것입니다.

    $ newline='
    > '
    
  4. 마지막으로 bash를 호출하여 교체를 수행합니다.

    $ bash -c "cat <<__EOF__${newline}$(cat template.txt)${newline}__EOF__"
    Hi, Bob
    
    Welcome to unix&linux
    

이것이 작동하는 이유는 무엇입니까? 아래에서 위로 분해해 보겠습니다. bash에 명령을 실행하도록 요청합니다( options 를 통해 -c). 그러나 변수 대체 ${newline}및 명령 확장은 $(cat ...)실제 실행 전에 상위 셸에서 발생합니다. bash -c ...따라서 결과는 bash -c줄바꿈이 포함된 명령 문자열과 전체 템플릿 텍스트를 보는 것입니다. 이는 마치 Bash 프롬프트에 다음을 입력한 것과 같습니다.

cat <<__EOF__
...contents of template.txt...
__EOF__

또한 대체 변수는 상위 쉘에 할당되기 때문에 내보내야 하지만 대체를 수행하는 것은 "하위" bash입니다.

노트그러나 이 접근 방식은 인용 문제로 쉽게 이어질 수 있습니다. 템플릿 텍스트는 bash 쉘에 의해 해석되므로,이후에 탈락함확장되어 시스템에서 명령이 실행될 수 있습니다.

답변3

eval실험하려면 작은따옴표로 묶인 문자열을 (이중) 포함할 수도 있습니다.

export HOSTNAME HOSTADDRESS
(
NL='
'
DELIM=$'\177'
esc="'\''"
export IFS=""
HOSTNAME="SH_SQL_00'8'9"
HOSTADDRESS='172.16.3.44 Bobby "drop" O'\''Tables ${PWD} $(ls)'
printf '%s\n' 'Hostname : $HOSTNAME' 'Host Address : $HOSTADDRESS' > template.txt
TEMPLATE="$(<template.txt)"
TEMPLATE="${TEMPLATE//\'/${esc}}"       # escape every single quote: ' --> '\'' (which later will become ''\''' by '${TEMPLATE}')
TEMPLATE="${TEMPLATE//${NL}/${DELIM}}"  # replace every newline char with a delim char
evalstr="echo '${TEMPLATE}'"
#printf '\n%s\n\n' "${evalstr}" | LC_ALL=C vis -fotc
set -xv
eval eval "${evalstr}"
) | LC_ALL=C tr '\177' '\n' | nl

답변4

MESSAGE=$(eval $(cat template.txt))

관련 정보