Bash의 eval 명령 및 일반적인 용도


165

bash 매뉴얼 페이지를 읽은 후이 게시물 과 관련하여 .

나는 eval명령이 정확히 무엇을 하고 어떤 것이 일반적인 용도 인지 이해하는 데 여전히 어려움을 겪고 있습니다. 예를 들어 다음과 같은 경우 :

bash$ set -- one two three  # sets $1 $2 $3
bash$ echo $1
one
bash$ n=1
bash$ echo ${$n}       ## First attempt to echo $1 using brackets fails
bash: ${$n}: bad substitution
bash$ echo $($n)       ## Second attempt to echo $1 using parentheses fails
bash: 1: command not found
bash$ eval echo \${$n} ## Third attempt to echo $1 using 'eval' succeeds
one

여기서 정확히 무슨 일이 일어나고 있으며 달러 기호와 백 슬래시는 어떻게 문제와 관련이 있습니까?


1
기록을 위해 두 번째 시도가 작동합니다. 서브 쉘에서 $($n)실행됩니다 $n. 1존재하지 않는 명령을 실행하려고 시도 합니다.
Martin Wickman

1
@MartinWickman 그러나 요구 사항은 echo $1결국이 아니라 실행하는 것 1입니다. 서브 쉘을 사용하여 수행 할 수 있다고 생각하지 않습니다.
Hari Menon


1
@ Raze2dust : 서브 쉘로 실행할 수 있다고 제안하지는 않지만 OP가 나열된 5 번째 명령이 작동하지 않는 이유를 설명합니다.
jedwards

답변:


196

eval문자열을 인수로 사용하여 명령 줄에 해당 문자열을 입력 한 것처럼 평가합니다. 여러 인수를 전달하면 먼저 인수 사이에 공백이 결합됩니다.

${$n}bash의 구문 오류입니다. 중괄호 안에 변수 이름과 접두사 및 접미사를 사용할 수 있지만 임의의 bash 구문을 사용할 수 없으며 특히 변수 확장을 사용할 수 없습니다. "이름이이 변수에있는 변수의 값"이라고 말하는 방법이 있습니다.

echo ${!n}
one

$(…)서브 쉘에서 괄호 안에 지정된 명령을 실행하고 (즉, 현재 쉘의 변수 값과 같은 모든 설정을 상속하는 별도의 프로세스에서) 출력을 수집합니다. 따라서 쉘 명령으로 echo $($n)실행 $n되며 출력을 표시합니다. 이후 $n로 평가 1, $($n)명령을 실행하려는 시도 1가 존재하지 않습니다.

eval echo \${$n}에 전달 된 매개 변수를 실행합니다 eval. 확장 후, 매개 변수는 echo${1} 입니다. 그래서 eval echo \${$n}명령을 실행echo ${1} .

대부분의 경우 변수 대체 및 명령 대체 (즉, a가있을 때 $)를 큰 따옴표로 묶어야합니다 "$foo", "$(foo)". 변수 및 명령 대체를 항상 큰 따옴표로 묶지 마십시오 . 큰 따옴표가 없으면 쉘은 필드 분할을 수행합니다 (즉, 변수 값 또는 명령의 출력을 개별 단어로 분할). 그런 다음 각 단어를 와일드 카드 패턴으로 처리합니다. 예를 들면 다음과 같습니다.

$ ls
file1 file2 otherfile
$ set -- 'f* *'
$ echo "$1"
f* *
$ echo $1
file1 file2 file1 file2 otherfile
$ n=1
$ eval echo \${$n}
file1 file2 file1 file2 otherfile
$eval echo \"\${$n}\"
f* *
$ echo "${!n}"
f* *

eval자주 사용되지 않습니다. 일부 쉘에서 가장 일반적으로 사용되는 것은 런타임까지 이름을 알 수없는 변수의 값을 얻는 것입니다. bash에서는 ${!VAR}구문 덕분에 필요하지 않습니다 . eval연산자, 예약어 등을 포함하는 더 긴 명령을 구성해야하는 경우에도 여전히 유용합니다.


위의 내 의견과 관련하여 평가자는 얼마나 많은 "패스"를 수행합니까?
kstratis

@ Konos5 어떤 의견? eval문자열 (구문 분석 및 평가의 결과 일 수 있음)을 수신하여 코드 스 니펫으로 해석합니다.
Gilles 'SO- 악의를 멈춰라'

Raze2dust의 답변 아래에 의견을 남겼습니다. 나는 eval이 대부분 역 참조 목적으로 사용된다고 믿는 경향이있다. eval echo \ $ {$ n}을 입력하면 하나를 얻습니다. 그러나 echo \ $ {$ n}을 입력하면 \ $ {1}이됩니다. 나는 이것이 eval의 "two-pass"구문 분석으로 인해 발생하고 있다고 생각합니다. 추가 i = n 선언을 사용하여 역 참조를 삼중 화해야하는 경우 어떻게 될지 궁금합니다. 이 경우 Raze2dust에 따르면 추가 평가를해야합니다. 그러나 더 좋은 방법이 있어야한다고 생각합니다 ... (쉽게 혼란
스러워

@ Konos5 나는 사용하지 않을 것이다 eval eval. 필요를 느낀 적이 기억 나지 않습니다. 실제로 두 번의 eval패스 가 필요한 경우 임시 변수를 사용하면 디버그하기가 더 쉽습니다 eval tmp="\${$i}"; eval x="\${$tmp}".
Gilles 'SO- 악마 그만해'

1
@ Konos5 "두 번 구문 분석"은 약간 잘못된 것입니다. 어떤 사람들은 다양한 확장으로부터 보호되는 Bash에서 리터럴 문자열 인수를 지정하기가 어렵 기 때문에 이것을 믿을 수도 있습니다. eval문자열로 코드를 가져 와서 일반적인 규칙에 따라 평가합니다. Bash가 eval의 인수에 대한 확장조차 수행하지 않도록 구문 분석을 수정하는 몇 가지 경우가 있기 때문에 기술적으로는 정확하지 않습니다.
ormaaj

39

eval을 "실행 전에 한 번 더 표현 평가"라고 생각하면됩니다.

eval echo \${$n}echo $11 차 평가 후 가됩니다 . 주목해야 할 세 가지 변경 사항 :

  • \$되었다 $(백 슬래시가 필요하다, 그렇지 않으면 평가하는 시도 ${$n}라는 변수 즉, {$n}허용되지 않습니다)
  • $n ~에 평가되었다 1
  • eval사라

두 번째 라운드에서는 기본적으로 echo $1 직접 실행할 수 있습니다.

따라서 eval <some command>먼저 평가합니다 <some command>(여기서는 대체 변수를 의미하고 이스케이프 문자를 올바른 문자로 대체하는 등)하고 결과 식을 다시 한 번 실행합니다.

eval변수를 동적으로 만들거나 이와 같이 읽도록 특별히 설계된 프로그램의 출력을 읽으려는 경우에 사용됩니다. 예는 http://mywiki.wooledge.org/BashFAQ/048 을 참조 하십시오 . 또한이 링크에는 일반적인 사용 방법 eval및 이와 관련된 위험이 포함되어 있습니다.


3
첫 번째 글 머리 기호에 대한 참고로 ${VAR}구문 허용되며 모호성이있을 때 선호됩니다 ( $VAR == $V, 뒤에 AR또는 $VAR == $VA뒤에 R). ${VAR}와 같습니다 $VAR. 실제로 변수 이름은 $n허용되지 않습니다.
jedwards

2
eval eval echo \\\${\${$i}}트리플 역 참조를 수행합니다. 더 간단한 방법이 있는지 확실하지 않습니다. 또한 내 컴퓨터에서 \${$n}잘 작동합니다 one.
Hari Menon

2
@ Konos5가 echo \\\${\${$i}}인쇄 \${${n}}됩니다. eval echo \\\${\${$i}}echo \${${n}}`` and prints $ {1} . eval eval echo \\\ $ {\ $ {$ i}}`에 해당 eval echo ${1}하고 prints와 같습니다 one.
Gilles 'SO- 악한 중지'

2
@ Konos5 같은 줄을 따라 생각하십시오-첫 번째 ` escapes the second one, and the third `는 그 $이후를 탈출합니다 . 그래서 그것은 \${${n}}한 라운드의 평가 이후 가됩니다
Hari Menon

2
@ Konos5 왼쪽에서 오른쪽은 따옴표 및 백 슬래시 구문 분석을위한 올바른 방법입니다. 먼저 \\ 하나의 백 슬래시를 생성합니다. 그런 다음 \$1 달러를 산출합니다. 등등.
Gilles 'SO- 악마 그만'

25

내 경험상, eval의 "일반적인"사용은 환경 변수를 설정하기 위해 쉘 명령을 생성하는 명령을 실행하는 것입니다.

환경 변수 모음을 사용하는 시스템이 있고 설정해야 할 값과 해당 값을 결정하는 스크립트 나 프로그램이있을 수 있습니다. 스크립트 나 프로그램을 실행할 때마다 포크 프로세스로 실행되므로 종료 될 때 환경 변수에 직접 수행하는 모든 작업이 손실됩니다. 그러나 해당 스크립트 나 프로그램은 내보내기 명령을 stdout으로 보낼 수 있습니다.

eval이 없으면 stdout을 임시 파일로 리디렉션하고 임시 파일을 소싱 한 다음 삭제해야합니다. eval을 사용하면 다음을 수행 할 수 있습니다.

eval "$(script-or-program)"

따옴표는 중요합니다. 이 (고려 된) 예제를 보자.

# activate.sh
echo 'I got activated!'

# test.py
print("export foo=bar/baz/womp")
print(". activate.sh")

$ eval $(python test.py)
bash: export: `.': not a valid identifier
bash: export: `activate.sh': not a valid identifier
$ eval "$(python test.py)"
I got activated!

이를 수행하는 일반적인 도구의 예가 있습니까? 도구 자체에는 평가판으로 전달할 수있는 쉘 명령 세트를 생성하는 수단이 있습니까?
Joakim Erdfelt

@Joakim 나는 그것을 수행하는 오픈 소스 도구를 모르지만, 내가 일한 회사의 일부 개인 스크립트에서 사용되었습니다. 방금 xampp와 함께이 기술을 다시 사용하기 시작했습니다. Apache .conf 파일은 작성된 환경 변수를 확장합니다 ${varname}. 환경 변수로 매개 변수화 된 몇 가지 사항만으로 여러 다른 서버에서 동일한 .conf 파일을 사용하는 것이 편리하다는 것을 알았습니다. / opt / lampp / xampp (아파치를 시작)를 편집하여 시스템을 찌르고 export.conf 파일의 변수를 정의하는 bash 문을 출력하는 스크립트로 이러한 종류의 평가를 수행 합니다.
sootsnoot

@Joakim 대안은 동일한 파킹을 기반으로 템플릿에서 영향을받는 각 .conf 파일을 생성하는 스크립트를 사용하는 것입니다. 내가 좋아하는 한 가지 방법은 / opt / lampp / xampp를 거치지 않고 아파치를 시작하면 오래된 출력 스크립트를 사용하지 않지만 환경 변수가 아무것도 확장되지 않고 유효하지 않은 지시문을 생성하기 때문에 시작되지 않는다는 것입니다.
sootsnoot

@Anthony Sottile $ (script-or-program) 주위에 따옴표를 추가하여 여러 명령을 실행할 때 중요하다고 답변을 편집 한 것을 볼 수 있습니다. foo.sh의 stdout에서 세미콜론으로 구분 된 명령을 사용하면 다음과 같이 작동합니다. echo '#! / bin / bash'> foo.sh; echo 'echo "echo -na; echo -nb; echo -n c"'>> foo.sh; chmod 755 foo.sh; eval $ (./ foo.sh). 이것은 stdout에서 abc를 생성합니다. ./foo.sh를 실행하면 다음이 생성됩니다. echo -na; 에코 -nb; echo -nc
sootsnoot

1
eval을 사용하는 일반적인 도구의 예는 pyenv를 참조하십시오 . pyenv를 사용하면 여러 버전의 Python간에 쉽게 전환 할 수 있습니다. 쉘 구성 파일에 넣 eval "$(pyenv init -)"습니다 .bash_profile. 작은 쉘 스크립트를 구성한 다음 현재 쉘에서이를 평가합니다.
Jerry101

10

eval 문은 쉘에게 eval의 인수를 명령으로 취하여 명령 행을 통해 실행하도록 지시합니다. 다음과 같은 상황에서 유용합니다.

스크립트에서 변수로 명령을 정의하고 나중에 해당 명령을 사용하려면 eval을 사용해야합니다.

/home/user1 > a="ls | more"
/home/user1 > $a
bash: command not found: ls | more
/home/user1 > # Above command didn't work as ls tried to list file with name pipe (|) and more. But these files are not there
/home/user1 > eval $a
file.txt
mailids
remote_cmd.sh
sample.txt
tmp
/home/user1 >

4

업데이트 : 어떤 사람들은 eval을 사용해서는 안된다고 말합니다. 동의하지 않습니다. 손상된 입력을에 전달할 수있을 때 위험이 발생한다고 생각합니다 eval. 그러나 위험하지 않은 많은 일반적인 상황이 있으므로 어떤 경우에도 eval을 사용하는 방법을 아는 것이 좋습니다. 이 stackoverflow 답변 은 평가의 위험과 평가의 대안을 설명합니다. 평가가 안전하고 효율적인지 여부를 결정하는 것은 궁극적으로 사용자의 몫입니다.


bash eval문을 사용하면 bash 스크립트로 계산하거나 얻은 코드 줄을 실행할 수 있습니다.

아마도 가장 간단한 예는 다른 bash 스크립트를 텍스트 파일로 열고 각 텍스트 줄을 읽고 eval순서대로 실행 하는 bash 프로그램 일 것입니다. 그것은 bash와 본질적으로 동일한 동작입니다.source가져온 스크립트의 내용에 대해 어떤 종류의 변환 (예 : 필터링 또는 대체)을 수행 할 필요가없는 한 문과 입니다.

나는 거의 필요하지 않지만 다른 변수에 할당 된 문자열에 이름 이 포함 된 eval변수를 읽거나 쓰는 것이 유용하다는 것을 알았습니다 . 예를 들어, 코드 풋 프린트를 작게 유지하면서 중복성을 피하면서 변수 세트에 대한 조치를 수행합니다.

eval개념적으로 간단합니다. 그러나 bash 언어의 엄격한 구문과 bash 인터프리터의 구문 분석 순서는 미묘한 차이가 있으며 eval암호로 보이며 사용하기 어렵거나 이해하기 어렵습니다. 필수 사항은 다음과 같습니다.

  1. 전달 된 인수 는 런타임에 계산 eval되는 문자열 표현식 입니다. eval인수의 최종 구문 분석 결과를 스크립트 의 실제 코드 줄로 실행합니다 .

  2. 구문 및 구문 분석 순서는 엄격합니다. 결과가 스크립트 범위에서 실행 가능한 bash 코드 행이 아닌 경우 프로그램은 eval가비지를 실행하려고 할 때 명령문에서 충돌 합니다.

  3. 테스트 할 때 eval명령문을 바꾸고 echo표시되는 것을 볼 수 있습니다 . 현재 컨텍스트에서 합법적 인 코드이면 코드를 실행하는 것이 eval좋습니다.


다음 예제는 eval의 작동 방식을 명확히하는 데 도움이 될 수 있습니다 ...

예 1 :

eval '정상적인'코드 앞에있는 진술은 NOP

$ eval a=b
$ eval echo $a
b

위의 예에서 첫 번째 eval진술은 목적이 없으며 제거 할 수 있습니다. eval코드에 대한 동적 측면이 없기 때문에 첫 번째 행에서 의미가 없습니다. 즉, 이미 bash 코드의 마지막 행으로 구문 분석되었으므로 bash 스크립트의 일반 코드 명령문과 동일합니다. 두 번째도 의미 eval가 없습니다. 왜냐하면 구문 분석 단계 $a가 문자 그대로의 문자열 로 변환 되기는하지만 간접적 인 것은 없습니다 (예 : 실제 bash 명사 또는 bash-held 스크립트 변수 의 문자열 값을 통한 참조 없음 ). eval접두사가 없는 코드 줄로 .



예 2 :

문자열 값으로 전달 된 var 이름을 사용하여 var 할당을 수행하십시오.

$ key="mykey"
$ val="myval"
$ eval $key=$val
$ echo $mykey
myval

에 있었다면 echo $key=$val출력은 다음과 같습니다.

mykey=myval

, 문자열 구문 분석의 최종 결과는 eval에 의해 실행될 것이므로 결국 echo 문의 결과는 ...



예 3 :

예 2에 더 많은 간접 추가

$ keyA="keyB"
$ valA="valB"
$ keyB="that"
$ valB="amazing"
$ eval eval \$$keyA=\$$valA
$ echo $that
amazing

위의 예제는 앞의 예제보다 약간 더 복잡하여 bash의 구문 분석 순서와 특성에 더 크게 의존합니다. eval라인은 대략 다음과 같은 순서로 내부적으로 분석 얻을 것이다 (다음 문 그냥 문이 최종 결과에 도달하기 위해 내부적으로 단계로 나누어받을 것입니다 방법을 보여 시도, 의사가 아닌 실제 코드이다 참고) .

 eval eval \$$keyA=\$$valA  # substitution of $keyA and $valA by interpreter
 eval eval \$keyB=\$valB    # convert '$' + name-strings to real vars by eval
 eval $keyB=$valB           # substitution of $keyB and $valB by interpreter
 eval that=amazing          # execute string literal 'that=amazing' by eval

가정 된 구문 분석 순서가 어떤 평가가 충분한 지 설명하지 못하면, 세 번째 예는 구문 분석을 더 자세히 설명하여 진행 상황을 명확하게 설명 할 수 있습니다.



예 4 :

이름 이 문자열에 포함 된 var 자체에 문자열 값이 포함되어 있는지 확인하십시오 .

a="User-provided"
b="Another user-provided optional value"
c=""

myvarname_a="a"
myvarname_b="b"
myvarname_c="c"

for varname in "myvarname_a" "myvarname_b" "myvarname_c"; do
    eval varval=\$$varname
    if [ -z "$varval" ]; then
        read -p "$varname? " $varname
    fi
done

첫 번째 반복에서 :

varname="myvarname_a"

배쉬의 인수를 구문 분석 eval하고, eval실행시 문자 그대로이보고 :

eval varval=\$$myvarname_a

다음 의사 코드 는 bash가 위의 실제 코드 줄을 해석하여에 의해 실행되는 최종 값에 도달 하는 방법 을 보여 줍니다 . (다음 줄은 정확한 bash 코드가 아니라 설명 적입니다) :eval

1. eval varval="\$" + "$varname"      # This substitution resolved in eval statement
2. .................. "$myvarname_a"  # $myvarname_a previously resolved by for-loop
3. .................. "a"             # ... to this value
4. eval "varval=$a"                   # This requires one more parsing step
5. eval varval="User-provided"        # Final result of parsing (eval executes this)

모든 파싱이 완료되면 결과가 실행되고 그 효과가 분명해 eval지며, 그 자체에 대해 특별히 신비한 것이 없음을 나타내며, 인수 의 파싱 에 복잡성이 있습니다.

varval="User-provided"

위 예제의 나머지 코드는 단순히 $ varval에 할당 된 값이 null인지 확인하여 값을 제공하라는 메시지를 표시합니다.


3

원래 의도적으로 eval을 사용하는 방법을 배운 적이 없었습니다. 대부분의 사람들은 전염병처럼 멀리하지 않는 것이 좋습니다. 그러나 최근에 더 빨리 인식하지 못하는 facepalm을 만드는 유스 케이스를 발견했습니다.

테스트하기 위해 대화식으로 실행하려는 cron 작업이있는 경우 cat을 사용하여 파일의 내용을보고 cron 작업을 복사하여 붙여 넣어 실행할 수 있습니다. 불행히도, 이것은 마우스를 터치하는 것과 관련이 있으며, 이것은 나의 책에서 죄입니다.

/etc/cron.d/repeatme에 ​​내용과 함께 cron 작업이 있다고 가정 해 보겠습니다.

*/10 * * * * root program arg1 arg2

모든 정크가있는 스크립트로 이것을 실행할 수는 없지만 cut을 사용하여 모든 정크를 제거하고 서브 쉘로 감싸고 eval로 문자열을 실행할 수 있습니다

eval $( cut -d ' ' -f 6- /etc/cron.d/repeatme)

cut 명령은 공백으로 구분 된 파일의 6 번째 필드 만 인쇄합니다. 그런 다음 Eval은 해당 명령을 실행합니다.

여기서는 cron 작업을 예로 사용했지만 개념은 stdout에서 텍스트를 형식화 한 다음 해당 텍스트를 평가하는 것입니다.

이 경우 eval을 사용하는 것은 안전하지 않습니다. 사전에 무엇을 평가할지 정확히 알기 때문입니다.


2

최근에 eval여러 개의 괄호 확장을 필요한 순서대로 평가하기 위해 사용해야했습니다. Bash는 왼쪽에서 오른쪽으로 여러 개의 괄호 확장을 수행하므로

xargs -I_ cat _/{11..15}/{8..5}.jpg

~로 확장

xargs -I_ cat _/11/8.jpg _/11/7.jpg _/11/6.jpg _/11/5.jpg _/12/8.jpg _/12/7.jpg _/12/6.jpg _/12/5.jpg _/13/8.jpg _/13/7.jpg _/13/6.jpg _/13/5.jpg _/14/8.jpg _/14/7.jpg _/14/6.jpg _/14/5.jpg _/15/8.jpg _/15/7.jpg _/15/6.jpg _/15/5.jpg

하지만 먼저 두 번째 괄호 확장이 필요했습니다.

xargs -I_ cat _/11/8.jpg _/12/8.jpg _/13/8.jpg _/14/8.jpg _/15/8.jpg _/11/7.jpg _/12/7.jpg _/13/7.jpg _/14/7.jpg _/15/7.jpg _/11/6.jpg _/12/6.jpg _/13/6.jpg _/14/6.jpg _/15/6.jpg _/11/5.jpg _/12/5.jpg _/13/5.jpg _/14/5.jpg _/15/5.jpg

내가 할 수있는 최선은

xargs -I_ cat $(eval echo _/'{11..15}'/{8..5}.jpg)

작은 따옴표는 eval명령 행을 구문 분석하는 동안 첫 번째 중괄호 세트가 확장되지 않도록 보호하여에 의해 호출 된 서브 쉘에 의해 확장되도록합니다 eval.

중첩 괄호 확장과 관련된 교활한 계획이있을 수 있습니다. 단순히이 작업을 수행 할 수는 있지만 너무 오래되어 어리석은 경우입니다.


1

일반적인 용도에 대해 물었습니다.

쉘 스크립팅에 대한 일반적인 불만 중 하나는 함수에서 값을 다시 얻기 위해 참조로 전달할 수 없다는 것입니다.

그러나 실제로 "eval"을 통해 참조로 전달할 수 있습니다 . 수신자는 발신자가 평가할 변수 할당 목록을 다시 전달할 수 있습니다. 호출자가 결과 변수의 이름을 지정할 수 있기 때문에 참조로 전달됩니다 (아래 예 참조). 오류 결과는 errno 및 errstr과 같은 표준 이름으로 다시 전달 될 수 있습니다.

다음은 bash에서 참조로 전달하는 예입니다.

#!/bin/bash
isint()
{
    re='^[-]?[0-9]+$'
    [[ $1 =~ $re ]]
}

#args 1: name of result variable, 2: first addend, 3: second addend 
iadd()
{
    if isint ${2} && isint ${3} ; then
        echo "$1=$((${2}+${3}));errno=0"
        return 0
    else
        echo "errstr=\"Error: non-integer argument to iadd $*\" ; errno=329"
        return 1
    fi
}

var=1
echo "[1] var=$var"

eval $(iadd var A B)
if [[ $errno -ne 0 ]]; then
    echo "errstr=$errstr"
    echo "errno=$errno"
fi
echo "[2] var=$var (unchanged after error)"

eval $(iadd var $var 1)
if [[ $errno -ne 0 ]]; then
    echo "errstr=$errstr"
    echo "errno=$errno"
fi  
echo "[3] var=$var (successfully changed)"

결과는 다음과 같습니다.

[1] var=1
errstr=Error: non-integer argument to iadd var A B
errno=329
[2] var=1 (unchanged after error)
[3] var=2 (successfully changed)

해당 텍스트 출력 에는 거의 무제한의 대역폭 이 있습니다 ! 여러 출력 라인을 사용하는 경우 더 많은 가능성이 있습니다. 예를 들어, 첫 번째 라인은 변수 할당에 사용될 수 있고, 두 번째 라인은 연속적인 '사고 흐름'에 사용될 수 있지만,이 포스트의 범위를 벗어납니다.


"더 많은 가능성"이 있다고 말하는 것은 사소하고 사소하며 중복이 가장 적습니다.
dotbit

0

"실행하기 전에 한 번 더 표현식을 평가하십시오"라는 대답이 마음에 들며 다른 예를 들어 설명하고 싶습니다.

var="\"par1 par2\""
echo $var # prints nicely "par1 par2"

function cntpars() {
  echo "  > Count: $#"
  echo "  > Pars : $*"
  echo "  > par1 : $1"
  echo "  > par2 : $2"

  if [[ $# = 1 && $1 = "par1 par2" ]]; then
    echo "  > PASS"
  else
    echo "  > FAIL"
    return 1
  fi
}

# Option 1: Will Pass
echo "eval \"cntpars \$var\""
eval "cntpars $var"

# Option 2: Will Fail, with curious results
echo "cntpars \$var"
cntpars $var

옵션 2의 흥미로운 결과는 다음과 같이 2 개의 매개 변수를 전달한 것입니다.

  • 첫 번째 매개 변수 : "value
  • 두 번째 매개 변수 : content"

카운터 직관적 인 방법은 무엇입니까? 추가 eval로 해결됩니다.

https://stackoverflow.com/a/40646371/744133 에서 적응


0

질문에서 :

who | grep $(tty | sed s:/dev/::)

파일 a 및 tty가 존재하지 않는다는 오류를 출력합니다. 나는 tty가 grep을 실행하기 전에 해석되지 않고 bash가 grep의 매개 변수로 tty를 파일 이름으로 해석 한 tty를 매개 변수로 전달했음을 의미한다는 것을 이해했습니다.

하위 프로세스를 지정해야하는 일치하는 괄호로 처리해야하는 중첩 된 재 지정 상황도 있지만, bash는 기본적으로 단어 구분 기호로 프로그램에 전송 될 매개 변수를 작성하므로 괄호가 먼저 일치하지 않지만 다음과 같이 해석됩니다. 본.

grep을 구체적으로 지정하고 파이프 대신 파일을 매개 변수로 지정했습니다. 또한 I / O 파이핑이 중첩되지 않도록 기본 명령을 단순화하여 명령의 출력을 파일로 전달했습니다.

grep $(tty | sed s:/dev/::) <(who)

잘 작동합니다.

who | grep $(echo pts/3)

실제로는 바람직하지 않지만 중첩 된 파이프를 제거하고 잘 작동합니다.

결론적으로, bash는 중첩 된 파이핑을 좋아하지 않는 것 같습니다. bash는 재귀 적으로 작성된 새로운 웨이브 프로그램이 아니라는 것을 이해하는 것이 중요합니다. 대신 bash는 기능이 추가 된 오래된 1,2,3 프로그램입니다. 이전 버전과의 호환성을 보장하기 위해 초기 해석 방식은 수정되지 않았습니다. 괄호를 먼저 괄호와 일치시키기 위해 bash를 재 작성한 경우, bash 프로그램의 수는 몇 개입니까? 많은 프로그래머들은 암호를 좋아합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.