쉘 스크립팅의 일반적인 규칙은 특별한 이유가 없는 한 변수를 항상 인용해야 한다는 것입니다. 더 자세한 내용을 알고 싶으시면 다음 Q&A를 확인하세요.bash/POSIX 쉘에서 변수를 인용하는 것을 잊어버리는 보안 위험.
그러나 다음과 같은 기능을 고려하십시오.
run_this(){
$@
}
거기에 인용 해야합니까 $@
? 나는 잠시 동안 놀았지만 따옴표 누락으로 인해 문제가 발생한 상황을 찾을 수 없었습니다. 반면에 따옴표를 사용하면 공백이 포함된 명령을 따옴표로 묶은 변수로 전달할 때 문제가 발생합니다.
#!/usr/bin/sh
set -x
run_this(){
$@
}
run_that(){
"$@"
}
comm="ls -l"
run_this "$comm"
run_that "$comm"
위 스크립트를 실행하면 다음이 반환됩니다.
$ a.sh
+ comm='ls -l'
+ run_this 'ls -l'
+ ls -l
total 8
-rw-r--r-- 1 terdon users 0 Dec 22 12:58 da
-rw-r--r-- 1 terdon users 45 Dec 22 13:33 file
-rw-r--r-- 1 terdon users 43 Dec 22 12:38 file~
+ run_that 'ls -l'
+ 'ls -l'
/home/terdon/scripts/a.sh: line 7: ls -l: command not found
run_that $comm
대신 을 사용하면 이 문제를 해결할 수 있지만 (참조되지 않은) 함수가 두 가지 모두에 대해 작동 run_that "$comm"
하므로 run_this
이것이 더 안전한 옵션인 것 같습니다.
그렇다면 $@
명령으로 실행되는 함수에 사용되는 구체적인 경우에는 인용부호로 묶어야 할까요? 인용해야 하거나 인용해서는 안 되는 이유를 설명하고 인용을 깨뜨릴 수 있는 데이터의 예를 제시하십시오.$@
$@
답변1
문제는 명령이 함수에 전달되는 방식입니다.
$ run_this ls -l Untitled\ Document.pdf
ls: cannot access Untitled: No such file or directory
ls: cannot access Document.pdf: No such file or directory
$ run_that ls -l Untitled\ Document.pdf
-rw------- 1 muru muru 33879 Dec 20 11:09 Untitled Document.pdf
"$@"
as in run_that
은 함수 앞에 일반적으로 작성된 명령(위에서 언급한 대로)이 접두사로 붙는 일반적인 경우에 사용해야 합니다.
따옴표가 없는 문자를 사용하려고 하면 공백이 포함된 파일 이름이 전달 $@
되지 run_this
않습니다. 다음 시도 중 어느 것도 작동하지 않습니다.
$ run_this 'ls -l Untitled\ Document.pdf'
ls: cannot access Untitled\: No such file or directory
ls: cannot access Document.pdf: No such file or directory
$ run_this 'ls -l "Untitled\ Document.pdf"'
ls: cannot access "Untitled\: No such file or directory
ls: cannot access Document.pdf": No such file or directory
$ run_this 'ls -l Untitled Document.pdf'
ls: cannot access Untitled: No such file or directory
ls: cannot access Document.pdf: No such file or directory
$ run_this 'ls -l' 'Untitled Document.pdf'
ls: cannot access Untitled: No such file or directory
ls: cannot access Document.pdf: No such file or directory
작동하지 않는 이유는 따옴표가 없는 확장이 단어 분할을 통과하여 공백으로 분할되고 따옴표 등을 해석하는 방법을 제공하지 않기 때문입니다(이를 위해서는 를 사용해야 함 eval
).
예를 들어 참조하십시오.
- 배쉬 FAQ 050(또는 "명령을 변수에 넣으려고 했지만 복잡한 경우가 항상 실패했습니다!")
- 변수에 저장된 명령을 어떻게 실행할 수 있나요?
답변2
그것은:
interpret_this_shell_code() {
eval "$1"
}
또는:
interpret_the_shell_code_resulting_from_the_concatenation_of_those_strings_with_spaces() {
eval "$@"
}
또는:
execute_this_simple_command_with_these_arguments() {
"$@"
}
하지만:
execute_the_simple_command_with_the_arguments_resulting_from_split+glob_applied_to_these_strings() {
$@
}
별 의미가 없습니다.
ls -l
명령을 실행하려면 ( 인수 ls
로 명령을 실행하지 않고 ) 다음을 수행할 수 있습니다.ls
-l
interpret_this_shell_code '"ls -l"'
execute_this_simple_command_with_these_arguments 'ls -l'
그러나 (가능성이 더 높음) 이것이 인수가 ls
포함된 명령 ls
인 경우 -l
다음을 실행합니다.
interpret_this_shell_code 'ls -l'
execute_this_simple_command_with_these_arguments ls -l
이제 단순한 명령 이상의 작업을 수행하고 변수 할당, 리디렉션, 파이핑을 수행하려면 다음을 수행하십시오 interpret_this_shell_code
.
interpret_this_shell_code 'ls -l 2> /dev/null'
물론 언제든지 이렇게 할 수 있습니다.
execute_this_simple_command_with_these_arguments eval '
ls -l 2> /dev/null'
답변3
bash/ksh/zsh의 관점에서 볼 때
$*
이들은 모두 $@
일반 배열 확장의 특수한 경우입니다. 배열 확장은 일반적인 변수 확장과 다릅니다.
$ a=("a b c" "d e" f)
$ printf ' -> %s\n' "${a[*]}"
-> a b c d e f
$ printf ' -> %s\n' "${a[@]}"
-> a b c
-> d e
-> f
$ printf ' -> %s\n' ${a[*]}
-> a
-> b
-> c
-> d
-> e
-> f
$ printf ' -> %s\n' ${a[@]}
-> a
-> b
-> c
-> d
-> e
-> f
$*
/ 확장을 사용하면 첫 번째 값 (기본적으로 공백) ${a[*]}
이 있는 배열을 하나의 거대한 문자열로 연결할 수 있습니다 . IFS
인용하지 않으면 일반 문자열처럼 분할됩니다.
$@
/ 확장 의 경우 / 확장이 인용되었는지 여부 ${a[@]}
에 따라 동작이 달라집니다 .$@
${a[@]}
"$@"
( 또는 ) 로 인용하면 또는 와"${a[@]}"
동등한 결과를 얻게 됩니다 ."$1" "$2" "$3" #...
"${a[1]}" "${a[2]}" "${a[3]}" # ...
$@
( 또는 ) 로 묶이지 않으면 또는${a[@]}
와 동등한 결과를 얻게 됩니다 .$1 $2 $3 #...
${a[1]} ${a[2]} ${a[3]} # ...
명령을 래핑하려면 반드시 필요합니다.인용하다 @확장(1.).
bash(및 bash 유사) 배열에 대한 추가 정보:https://lukeshu.com/blog/bash-arrays.html
답변4
변수를 실행하는 것은 bash
실패하기 쉬운 기술입니다. run_this
다음과 같은 모든 극단적인 경우를 올바르게 처리하는 함수를 작성하는 것은 불가능합니다.
- 파이프(예
ls | grep filename
: ) - 입력/출력 리디렉션(예
ls > /dev/null
: ) if
while
다음과 같은 쉘 문
코드 중복을 피하고 싶다면 함수를 사용하는 것이 좋습니다. 예를 들면 다음과 같습니다.
run_this(){
"$@"
}
command="ls -l"
...
run_this "$command"
너는 써야 해
command() {
ls -l
}
...
command
이러한 명령을 런타임에만 사용할 수 있는 경우 eval
오류를 일으키는 모든 문제를 처리하도록 특별히 설계된 명령 을 사용해야 합니다 run_this
.
command="ls -l | grep filename > /dev/null"
...
eval "$command"
참고 eval
하세요모두 다 아는보안상의 이유로 신뢰할 수 없는 소스에서 변수가 전달되는 경우 run_this
임의 코드 실행에 노출될 수도 있습니다 .