bash가 파일에서로드 된 문자열에서 변수를 확장하도록 강제


88

bash (강제?)를 문자열 (파일에서로드 된)에서 확장 변수로 만드는 방법을 찾으려고합니다.

내용이 포함 된 "something.txt"라는 파일이 있습니다.

hello $FOO world

그런 다음 실행

export FOO=42
echo $(cat something.txt)

이것은 다음을 반환합니다.

   hello $FOO world

변수가 설정되어 있어도 $ FOO를 확장하지 않았습니다. 파일을 평가하거나 소싱 할 수 없습니다. 파일을 시도하고 실행할 수 있기 때문입니다 (그대로 실행 가능하지 않습니다. 변수가 삽입 된 문자열을 원합니다).

어떤 아이디어?



아래 답변에 대한 설명을 의미 했습니까?
Michael Neale

당신을위한 코멘트를 의미했습니다. 하나 이상의 답변이의 사용을 제안하고 eval그 의미를 인식하는 것이 중요합니다.
추후 공지가있을 때까지 일시 중지되었습니다.

@DennisWilliamson gotcha-답장이 조금 늦었지만 좋은 팁입니다. 그리고 물론 그 사이에 "쉘 쇼크"와 같은 일이 있었기 때문에 귀하의 의견은 시간의 시험을 견뎌냈습니다! 팁 모자
마이클 닐

이것이 귀하의 질문에 대답합니까? 변수에서 Bash 확장 변수
LoganMzz

답변:


111

나는이 질문에 대한 답이 바로 envsubst명령 이라고 생각하는 것을 우연히 발견했습니다 .

envsubst < something.txt

배포판에서 아직 사용할 수없는 경우

GNU 패키지 gettext.

@Rockallite- '\ $'문제를 처리하기 위해 작은 래퍼 스크립트를 작성했습니다.

(BTW, 입력의 일부 변수 만 확장하기 위해 https://unix.stackexchange.com/a/294400/7088 에 설명 된 envsubst의 "기능"이 있지만 예외를 이스케이프하는 것이 훨씬 더 많다는 데 동의합니다. 편리한.)

내 스크립트는 다음과 같습니다.

#! /bin/bash
      ## -*-Shell-Script-*-
CmdName=${0##*/}
Usage="usage: $CmdName runs envsubst, but allows '\$' to  keep variables from
    being expanded.
  With option   -sl   '\$' keeps the back-slash.
  Default is to replace  '\$' with '$'
"

if [[ $1 = -h ]]  ;then echo -e >&2  "$Usage" ; exit 1 ;fi
if [[ $1 = -sl ]] ;then  sl='\'  ; shift ;fi

sed 's/\\\$/\${EnVsUbDolR}/g' |  EnVsUbDolR=$sl\$  envsubst  "$@"

5
+1. 이 있어요 유일한 대답은 보장 명령 치환, 인용 제거, 인수 처리 등을 수행하지
조쉬 켈리

6
(bash에서) 완전히 내 보낸 쉘 변수에 대해서만 작동합니다. 예 : S = '$ a $ b'; a = 세트; 수출 b = 내보내기; echo $ S | envsubst; 수익률 : "export"
gaoithe aug

1
고마워요-몇 년이 지나면이 대답을 받아 들여야한다고 생각합니다.
Michael Neale

1
그러나 달러 기호 ( $) 이스케이프를 처리하지 않습니다 . 예를 들어 echo '\$USER' | envsubst접두사가 아닌 백 슬래시가있는 현재 사용자 이름을 출력합니다 $USER.
Rockallite 2017-04-08

3
홈브류가 필요한 Mac에서 이것을 얻는 것 같습니다brew link --force gettext
KeepCalmAndCarryOn dec

25

많은 답변 evalecho작업 종류가 있지만 여러 줄, 쉘 메타 문자 이스케이프 시도, bash에 의해 확장되지 않은 템플릿 내부의 이스케이프 등과 같은 다양한 작업을 중단합니다.

나는 같은 문제가 있었고 내가 말할 수있는 한 모든 것을 올바르게 처리하는이 쉘 함수를 작성했습니다. 이것은 bash의 명령 대체 규칙으로 인해 템플릿에서 후행 줄 바꿈 만 제거하지만 다른 모든 것이 그대로 유지되는 한 문제가되지는 않았습니다.

apply_shell_expansion() {
    declare file="$1"
    declare data=$(< "$file")
    declare delimiter="__apply_shell_expansion_delimiter__"
    declare command="cat <<$delimiter"$'\n'"$data"$'\n'"$delimiter"
    eval "$command"
}

예를 들어, parameters.cfg변수를 설정하는 쉘 스크립트 인 a와 template.txt해당 변수를 사용하는 템플릿 인 a 와 함께 다음과 같이 사용할 수 있습니다 .

. parameters.cfg
printf "%s\n" "$(apply_shell_expansion template.txt)" > result.txt

실제로 저는 이것을 일종의 경량 템플릿 시스템으로 사용합니다.


4
이것은 내가 지금까지 찾은 최고의 트릭 중 하나입니다 :). 귀하의 답변에 따라 다른 변수에 대한 참조를 포함하는 문자열로 변수를 확장하는 함수를 구축하는 것은 매우 쉽습니다. 함수에서 $ data를 $ 1로 바꾸면됩니다. 이것이 원래의 질문 인 BTW에 대한 최선의 대답이라고 생각합니다.
은하

이것에도 일반적인 eval함정 이 있습니다 . ; $(eval date)입력 파일 내부의 모든 줄 끝에 추가 를 시도하면 date명령 이 실행 됩니다.
anubhava

1
@anubhava 무슨 뜻인지 잘 모르겠습니다. 이 경우 실제로 원하는 확장 동작입니다. 즉, 네, 이것으로 임의의 쉘 코드를 실행할 수 있어야합니다.
wjl

22

당신은 시도 할 수 있습니다

echo $(eval echo $(cat something.txt))

9
echo -e "$(eval "echo -e \"`<something.txt`\"")"새 줄을 유지해야하는 경우 사용하십시오 .
pevik 2013-09-13

작품 매력
KYB

1
그러나 당신 template.txt이 텍스트 인 경우에만 . 이 작업은 명령이있는 경우 실행 (평가)하므로 위험합니다.
kyb

2
그러나 이것은 큰 따옴표를 제거
토마스 Decaux에게

서브 쉘을 비난하십시오.
ingyhere

8

각 줄을 인쇄 하는 것이 아니라 Bash가 변수 대체를 수행 할 수 있도록 평가 하고 싶습니다 .

FOO=42
while read; do
    eval echo "$REPLY"
done < something.txt

자세한 내용은 help eval또는 Bash 설명서를 참조하십시오.


2
eval위험하다 . 당신은뿐만 아니라 취득 $varname(당신이와 마찬가지로 확장 envsubst), 그러나 $(rm -rf ~)뿐만 아니라.
Charles Duffy

4

또 다른 접근 방식 (이상해 보이지만 어쨌든 여기에 넣습니다) :

echo 문을 감싸고 something.txt의 내용을 임시 파일에 씁니다.

something=$(cat something.txt)

echo "echo \"" > temp.out
echo "$something" >> temp.out
echo "\"" >> temp.out

그런 다음 다시 변수로 소싱하십시오.

RESULT=$(source temp.out)

$ RESULT는 모두 확장됩니다. 그러나 그것은 너무 잘못된 것 같습니다!


1
icky이지만 가장 간단하고 간단하며 작동합니다.
kyb

3

변수 참조가 확장 되기만 원하면 (내가 가지고있는 목표) 아래를 수행 할 수 있습니다.

contents="$(cat something.txt)"
echo $(eval echo \"$contents\")

($ contents 주변의 이스케이프 따옴표가 여기에서 핵심입니다)


나는 이것을 시도했지만 $ ()는 결과 문자열을 끊는 내 경우 줄 끝을 제거합니다
moz

eval 줄을로 변경하고 eval "echo \"$$contents\""공백을 보존했습니다
moz

1

다음 솔루션 :

  • 정의 된 변수를 대체 할 수 있습니다.

  • 정의되지 않은 변경되지 않은 변수 자리 표시자를 남깁니다. 이것은 자동화 된 배포 중에 특히 유용합니다.

  • 다음 형식의 변수 대체를 지원합니다.

    ${var_NAME}

    $var_NAME

  • 환경에 정의되지 않은 변수를보고하고 이러한 경우 오류 코드를 반환합니다.



    TARGET_FILE=someFile.txt;
    ERR_CNT=0;

    for VARNAME in $(grep -P -o -e '\$[\{]?(\w+)*[\}]?' ${TARGET_FILE} | sort -u); do     
      VAR_VALUE=${!VARNAME};
      VARNAME2=$(echo $VARNAME| sed -e 's|^\${||g' -e 's|}$||g' -e 's|^\$||g' );
      VAR_VALUE2=${!VARNAME2};

      if [ "xxx" = "xxx$VAR_VALUE2" ]; then
         echo "$VARNAME is undefined ";
         ERR_CNT=$((ERR_CNT+1));
      else
         echo "replacing $VARNAME with $VAR_VALUE2" ;
         sed -i "s|$VARNAME|$VAR_VALUE2|g" ${TARGET_FILE}; 
      fi      
    done

    if [ ${ERR_CNT} -gt 0 ]; then
        echo "Found $ERR_CNT undefined environment variables";
        exit 1 
    fi

1
  1. 경우 something.txt은 오직이 하나 개의 라인, bash방법, (짧은 버전 마이클 닐의 "구역질"대답 )를 사용하여 공정명령 대체를 :

    FOO=42 . <(echo -e echo $(<something.txt))
    

    산출:

    hello 42 world
    

    참고 export필요하지 않습니다.

  2. 경우 something.txt은 하나 또는 그 이상의 선하는이 GNU를 평가할의 방법 :sed e

    FOO=42 sed 's/"/\\\"/g;s/.*/echo "&"/e' something.txt
    

1
sed내 목적에 가장 적합한 평가를 찾았습니다 . 다른 구성 파일로 내 보낸 변수에서 값을 채운 템플릿에서 스크립트를 생성해야했습니다. 명령 확장을 위해 적절하게 이스케이프 처리합니다. 예를 들어 \$(date)템플릿에 넣어 $(date)출력 을 가져옵니다 . 감사!
gadamiak

0
$ eval echo $(cat something.txt)
hello 42 world
$ bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin17)
Copyright (C) 2007 Free Software Foundation, Inc.

0
foo=45
file=something.txt       # in a file is written: Hello $foo world!
eval echo $(cat $file)

1
제한적이고 즉각적인 도움을 제공 할 수있는이 코드 스 니펫에 감사드립니다. 적절한 설명은 크게이 문제에 대한 좋은 해결책이 왜 보여 장기적인 가치를 향상 것이고, 다른 유사한 질문을 미래의 독자들에게 더 유용 할 것입니다. 제발 편집 당신이 만든 가정 등 일부 설명을 추가 할 답변을.
Capricorn

-1

envsubst 대체하는 콘텐츠가 "합리적인"길이 인 경우 훌륭한 솔루션입니다 (LenW의 답변 참조).

제 경우에는 변수 이름을 대체하기 위해 파일의 내용을 대체해야했습니다. envsubst콘텐츠를 환경 변수로 내 보내야하며 bash는 1MB 정도가 넘는 환경 변수를 내보낼 때 문제가 있습니다.

awk 솔루션

다른 질문에서 cuonglm의 솔루션 사용 :

needle="doc1_base64" # The "variable name" in the file. (A $ is not needed.)
needle_file="doc1_base64.txt" # Will be substituted for the needle 
haystack=$requestfile1 # File containing the needle
out=$requestfile2
awk "BEGIN{getline l < \"${needle_file}\"}/${needle}/{gsub(\"${needle}\",l)}1" $haystack > $out

이 솔루션은 대용량 파일에도 적용됩니다.


-3

다음 작업 : bash -c "echo \"$(cat something.txt)"\"


이것은 비효율적 일뿐만 아니라 매우 위험합니다. 같은 안전한 확장을 실행하는 $foo것이 아니라 $(rm -rf ~).
Charles Duffy
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.