실행 중에 변수를 인용해야 합니까?

실행 중에 변수를 인용해야 합니까?

쉘 스크립팅의 일반적인 규칙은 특별한 이유가 없는 한 변수를 항상 인용해야 한다는 것입니다. 더 자세한 내용을 알고 싶으시면 다음 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).

예를 들어 참조하십시오.

답변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[@]}

  1. "$@"( 또는 ) 로 인용하면 또는 와 "${a[@]}"동등한 결과를 얻게 됩니다 ."$1" "$2" "$3" #..."${a[1]}" "${a[2]}" "${a[3]}" # ...
  2. $@( 또는 ) 로 묶이지 않으면 또는 ${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임의 코드 실행에 노출될 수도 있습니다 .

관련 정보