Linux Bash 스크립트에서 eval을 사용하는 방법
모든 Bash 명령 중에서 좋지 않은 오래된 eval
이 아마도 최악의 평판을 가지고 있을 것입니다. 정당한가, 아니면 그냥 나쁜 언론? 가장 사랑받지 못하는 Linux 명령의 사용과 위험에 대해 논의합니다.
eval에 대해 이야기해야 합니다.
부주의하게 사용하면 eval
은 예측할 수 없는 동작과 심지어 시스템 불안정을 초래할 수 있습니다. 그것의 소리에서 우리는 그것을 사용하지 말아야 할 것입니다, 그렇죠? 글쎄요.
자동차에 대해서도 비슷한 말을 할 수 있습니다. 잘못된 손에 들어가면 치명적인 무기가 됩니다. 사람들은 그들을 램레이드와 도주 차량으로 사용합니다. 우리 모두 자동차 사용을 중단해야 할까요? 아니, 당연하지. 그러나 그것들은 적절하게 사용되어야 하며 그것들을 운전하는 방법을 아는 사람들에 의해 사용되어야 합니다.
eval
에 적용되는 일반적인 형용사는 evil입니다. 그러나 그것은 모두 그것이 어떻게 사용되는지에 달려 있습니다. eval
명령은 하나 이상의 변수에서 값을 수집합니다. 명령 문자열을 생성합니다. 그런 다음 해당 명령을 실행합니다. 이는 스크립트 실행 중에 명령 내용이 동적으로 파생되는 상황에 대처해야 할 때 유용합니다.
스크립트 외부 어딘가에서 수신한 문자열에 eval
을 사용하도록 스크립트를 작성할 때 문제가 발생합니다. 사용자가 입력하거나, API를 통해 전송하거나, HTTPS 요청에 태그를 지정하거나, 스크립트 외부의 다른 곳에서 사용할 수 있습니다.
eval
이 작동할 문자열이 로컬에서 프로그래밍 방식으로 파생되지 않은 경우 문자열에 내장된 악성 명령이나 기타 잘못된 형식의 입력이 포함될 위험이 있습니다. 당연히 eval
이 악의적인 명령을 실행하는 것을 원하지 않습니다. 따라서 안전을 위해 외부에서 생성된 문자열이나 사용자 입력과 함께 eval
을 사용하지 마십시오.
eval을 사용한 첫 단계
eval
명령은 내장 Bash 셸 명령입니다. Bash가 있으면 eval
이 나타납니다.
eval
은 매개변수를 단일 문자열로 연결합니다. 단일 공백을 사용하여 연결된 요소를 구분합니다. 인수를 평가한 다음 전체 문자열을 실행할 쉘로 전달합니다.
wordcount
라는 변수를 만들어 봅시다.
wordcount="wc -w raw-notes.md"
문자열 변수에는 raw-notes.md라는 파일의 단어 수를 세는 명령이 포함되어 있습니다.
eval
을 사용하여 변수의 값을 전달하여 해당 명령을 실행할 수 있습니다.
명령은 하위 쉘이 아닌 현재 쉘에서 실행됩니다. 우리는 이것을 쉽게 보여줄 수 있습니다. variables.txt라는 짧은 텍스트 파일이 있습니다. 이 두 줄이 포함되어 있습니다.
first=How-To second=Geek
cat
을 사용하여 이 줄을 터미널 창으로 보냅니다. 그런 다음 eval
을 사용하여 cat
명령을 평가하여 텍스트 파일 내의 지침이 실행되도록 합니다. 그러면 변수가 설정됩니다.
cat variables.txt eval "$(cat variables.txt)" echo $first $second
echo
를 사용하여 변수 값을 인쇄하면 eval
명령이 하위 쉘이 아닌 현재 쉘에서 실행되는 것을 볼 수 있습니다.
하위 셸의 프로세스는 상위 셸 환경을 변경할 수 없습니다. eval은 현재 셸에서 실행되기 때문에 eval
에 의해 설정된 변수는 eval
명령을 실행한 셸에서 사용할 수 있습니다.
스크립트에서 eval
을 사용하는 경우 eval
에 의해 변경되는 셸은 스크립트를 시작한 셸이 아니라 스크립트가 실행 중인 하위 셸입니다.
명령 문자열에서 변수 사용
명령 문자열에 다른 변수를 포함할 수 있습니다. 정수를 저장하기 위해 두 개의 변수를 설정합니다.
num1=10 num2=7
두 숫자의 합을 반환하는 expr
명령을 저장할 변수를 생성합니다. 이는 명령에서 두 정수 변수의 값에 액세스해야 함을 의미합니다. expr
문 주위의 백틱에 유의하십시오.
add="`expr $num1 + $num2`"
expr
문의 결과를 표시하는 다른 명령을 생성합니다.
show="echo"
echo
문자열의 끝이나 expr
문자열의 시작 부분에는 공백을 포함할 필요가 없습니다. eval
이 이를 처리합니다.
전체 명령을 실행하기 위해 다음을 사용합니다.
eval $show $add
expr
문자열 내부의 변수 값은 실행할 쉘로 전달되기 전에 eval
에 의해 문자열로 대체됩니다.
변수 내부의 변수에 접근하기
변수에 값을 할당한 다음 해당 변수의 이름을 다른 변수에 할당할 수 있습니다. eval
을 사용하면 두 번째 변수에 저장된 값이라는 이름에서 첫 번째 변수에 있는 값에 액세스할 수 있습니다. 예를 들어 문제를 푸는 데 도움이 될 것입니다.
이 스크립트를 편집기에 복사하고 assign.sh라는 파일로 저장합니다.
#!/bin/bash title="How-To Geek" webpage=title command="echo" eval $command \${$webpage}
chmod
명령으로 실행 가능하게 만들어야 합니다.
chmod +x assign.sh
이 문서에서 복사하는 모든 스크립트에 대해 이 작업을 수행해야 합니다. 각각의 경우에 적절한 스크립트 이름을 사용하십시오.
스크립트를 실행할 때 eval
명령이 webpage
변수를 사용하고 있어도 title
변수의 텍스트를 볼 수 있습니다.
./assign.sh
이스케이프된 달러 기호 $
및 중괄호 {}
로 인해 eval은 webpage
변수.
동적으로 생성된 변수 사용
eval
을 사용하여 동적으로 변수를 생성할 수 있습니다. 이 스크립트를 loop.sh라고 합니다.
#!/bin/bash total=0 label="Looping complete. Total:" for n in {1..10} do eval x$n=$n echo "Loop" $x$n ((total+=$x$n)) done echo $x1 $x2 $x3 $x4 $x5 $x6 $x7 $x8 $x9 $x10 echo $label $total
우리가 생성한 변수 값의 합계를 보유하는 total
이라는 변수를 생성합니다. 그런 다음 label
이라는 문자열 변수를 생성합니다. 이것은 간단한 텍스트 문자열입니다.
우리는 10번 반복하고 x1
에서 x10
까지 10개의 변수를 생성할 것입니다. 루프 본문의 eval
문은 x를 제공하고 루프 카운터 $n
의 값을 가져와 변수 이름을 만듭니다. 동시에 새 변수를 루프 카운터 $n
의 값으로 설정합니다.
새 변수를 터미널 창에 인쇄한 다음 total
변수를 새 변수 값으로 증가시킵니다.
루프 외부에서 10개의 새 변수가 다시 한 번 모두 한 줄에 인쇄됩니다. 이름의 계산 또는 파생 버전을 사용하지 않고 실제 이름으로도 변수를 참조할 수 있습니다.
마지막으로 total
변수의 값을 인쇄합니다.
./loop.sh
배열과 함께 eval 사용
오래 실행되고 일부 처리를 수행하는 스크립트가 있는 시나리오를 상상해 보십시오. 타임스탬프에서 생성된 이름으로 로그 파일에 씁니다. 경우에 따라 새 로그 파일을 시작합니다. 스크립트가 완료되면 오류가 없으면 생성한 로그 파일을 삭제합니다.
단순히 rm *.log
하는 것이 아니라 생성한 로그 파일만 삭제하는 것이 좋습니다. 이 스크립트는 해당 기능을 시뮬레이트합니다. 이것은 clear-logs.sh입니다.
#!/bin/bash declare -a logfiles filecount=0 rm_string="echo" function create_logfile() { ((++filecount)) filename=$(date +"%Y-%m-%d_%H-%M-%S").log logfiles[$filecount]=$filename echo $filecount "Created" ${logfiles[$filecount]} } # body of the script. Some processing is done here that # periodically generates a log file. We'll simulate that create_logfile sleep 3 create_logfile sleep 3 create_logfile sleep 3 create_logfile # are there any files to remove? for ((file=1; file<=$filecount; file++)) do # remove the logfile eval $rm_string ${logfiles[$file]} "deleted..." logfiles[$file]="" done
스크립트는 logfiles
라는 배열을 선언합니다. 이것은 스크립트에 의해 생성된 로그 파일의 이름을 보유합니다. filecount
라는 변수를 선언합니다. 이것은 생성된 로그 파일의 수를 보유합니다.
또한 rm_string
이라는 문자열을 선언합니다. 실제 스크립트에서는 rm
명령이 포함되지만 echo
를 사용하여 비파괴적인 방식으로 원리를 설명할 수 있습니다.
create_logfile()
함수는 각 로그 파일의 이름이 지정되고 열리는 위치입니다. 파일 이름만 생성하고 파일 시스템에서 생성된 것처럼 가장합니다.
함수는 filecount
변수를 증가시킵니다. 초기 값은 0이므로 생성한 첫 번째 파일 이름은 배열의 위치 1에 저장됩니다. 이것은 의도적으로 수행되며 나중에 참조하십시오.
파일 이름은 date
명령과 .log 확장자를 사용하여 생성됩니다. 이름은 filecount
로 표시된 위치의 배열에 저장됩니다. 이름이 터미널 창에 인쇄됩니다. 실제 스크립트에서는 실제 파일도 생성합니다.
스크립트 본문은 sleep
명령을 사용하여 시뮬레이트됩니다. 첫 번째 로그 파일을 만들고 3초 동안 기다린 다음 다른 파일을 만듭니다. 파일 이름의 타임스탬프가 서로 다르도록 간격을 두고 4개의 로그 파일을 만듭니다.
마지막으로 로그 파일을 삭제하는 루프가 있습니다. 루프 카운터 파일은 1로 설정됩니다. 생성된 파일 수를 포함하는 filecount
값까지 계산됩니다.
로그 파일이 생성되지 않았기 때문에 filecount
가 여전히 0으로 설정되어 있으면 1이 0보다 작거나 같지 않기 때문에 루프 본문이 실행되지 않습니다. 이것이 filecount
변수가 선언될 때 0으로 설정되고 첫 번째 파일이 생성되기 전에 증가된 이유입니다.
루프 내에서 eval
을 비파괴 rm_string
및 배열에서 검색된 파일 이름과 함께 사용합니다. 그런 다음 배열 요소를 빈 문자열로 설정합니다.
이것은 우리가 스크립트를 실행할 때 우리가 보는 것입니다.
./clear-logs.sh
모두 나쁘지는 않습니다
많은 비방을 받는 eval
은 확실히 용도가 있습니다. 대부분의 도구와 마찬가지로 무분별하게 사용하면 여러 가지 면에서 위험합니다.
작동하는 문자열이 내부적으로 생성되고 사람, API 또는 HTTPS 요청과 같은 것에서 캡처되지 않는지 확인하면 주요 함정을 피할 수 있습니다.