아래는 내가 실행하려는 기능입니다. 문제는 main_cmd
다른 명령의 별칭과 관련된 것 같습니다. 나는 이것이 서브쉘을 생성할 때의 문제일 뿐이라고 생각하는데, 단순한 배열 확장에서는 왜 이런 일이 발생합니까? IIUC 하위 쉘이 생성되지 않습니다.
my_function() {
... code to set opt1 based on $1 ...
cmd=(
main_cmd hard_coded_arg_1 hard_coded_arg_2
-a "$opt1"
-b hard_coded_opt
"'$2'"
)
# Doesn't work.
"$cmd[@]"
# Works
eval "$cmd[@]"
}
그냥 사용하고 싶은데 eval
반패턴인 것 같네요(왜 표현에만 중요한지 이해는 안 되지만)나처형될 것이다곧장, 그러나 그것은 또 다른 주제입니다).
편집: 이것을 명확히 하기 위해zsh
답변1
별칭은 특별합니다. 구문 분석이 완전히 완료되기 전에 명령줄 처리 초기에 처리되며 별도의 함수를 호출하는 대신 직접 텍스트 교체처럼 작동합니다.
별칭을 사용하면 다음을 수행할 수 있습니다.
% alias maybe=if
% maybe true; then echo yes; fi
yes
또는
% alias foo='echo hi; '
% foo echo bar
hi
bar
심지어
% alias xyz='echo "foo'
% xyz bar"
foo bar
두 번째 경우에는 foo echo bar
번역된 echo hi; echo bar
다음 일반적인 방식으로 구문 분석됩니다. 함수의 경우 명령 이름과 두 개의 매개변수로 구문 분석된 후 쉘은 나중에 그것이 foo
함수인지, 내장 명령인지, 외부 명령인지 결정해야 합니다. 마지막 경우, xyz bar"
별칭이 존재하지 않으면 명령에 닫히지 않은 따옴표가 표시됩니다.
위의 예제 중 어느 것도 함수와 함께 작동하지 않습니다. (글쎄, 어쨌든 그들은 일종의 "작동"을 합니다. 누군가는 그들이 더 비슷하다고 말할 수도 있습니다.나머지쉘 구문. )
그러나 이는 "${cmd[@]}"
구문 분석 및 확장 후에 별칭 처리가 오래 전에 사라졌음을 의미하기도 합니다. 실제로 다음과 같이 할 수 있습니다.
% alias runcmd='"${cmd[@]}"'
% cmd=(printf "'%s'\n" "foo bar" abc)
% runcmd
'foo bar'
'abc'
이는 어레이 확장이 완료되기 전에 별칭이 처리되었음을 나타냅니다.
를 사용하면 별칭 확장을 포함하여 또 다른 명령줄 처리를 강제한다는 eval
점에서 "작동"합니다 . eval
하지만 실제로는 원하지 않는 명령 매개변수의 따옴표 처리도 포함됩니다. 바라보다스테판의 자세한 답변이 점에 대해서.
main_cmd
별칭 대신 함수를 사용하면 덜 혼란스러울 수 있습니다.
답변2
당신이 원하는 것:
cmd=(
main_cmd hard_coded_arg_1 hard_coded_arg_2
-a "$opt1"
-b hard_coded_opt
"$2"
)
"${cmd[@]}"
다음 요소를 포함하는 배열을 정의합니다.
main_cmd
hard_coded_arg_1
hard_coded_arg_2
-a
value-of-opt1-variable
-b
hard_coded_opt
value-of-second-positional-parameter
그런 다음 이는 각 인수로 개별적으로 확장 "${cmd[@]}"
(또는 zsh에서)되므로 배열의 모든 요소를 인수로 사용하여 배열의 "$cmd[@]"
첫 번째 요소( )에서 찾는 명령을 실행하게 됩니다 .$cmd[1]
zsh에서는 just를 사용하는 것도 $cmd
여기에서 작동하지만 빈 요소를 제거하므로 임의의 명령과 함께 사용할 수 없습니다.
또는 (배열을 사용하는 것은 거의 의미가 없지만):
cmd=(
'main_cmd hard_coded_arg_1 hard_coded_arg_2'
'-a "$opt1"'
'-b hard_coded_opt'
'"$2"'
)
eval " ${cmd[@]}"
어레이에는 다음이 포함됩니다.
main_cmd hard_coded_arg_1 hard_coded_arg_2
-a "$opt1"
-b hard_coded_opt
"$2"
우리는 eval
이를 별도의 인수로 전달합니다(옵션이 될 수 있는 a가 포함된 경우 첫 번째 인수 앞에 공백을 추가 -
) eval
. 중간에 공백과 연결하면 다음이 제공됩니다. main_cmd hard_coded_arg_1 hard_coded_arg_2 -a "$opt1" -b hard_coded_opt "$2"
그런 다음 쉘 코드로 해석됩니다. 하다 eval
).
행위:
cmd=( main_cmd hard_coded_arg_1 hard_coded_arg_2 -a "$opt1" -b hard_coded_opt "'$2'" ) eval "$cmd[@]"
당신이 얻는 배열의 요소는 다음과 같기 때문에 잘못되었습니다.
main_cmd
hard_coded_arg_1
hard_coded_arg_2
-a
value-of-opt1-variable
-b
hard_coded_opt
'value-of-second-positional-parameter'
그래서 당신은 설명을 끝내게 됩니다 eval
:
main_cmd hard_coded_arg_1 hard_coded_arg_2 -a value-of-opt1-variable -b hard_coded_opt 'value-of-second-positional-parameter'
만약 value-of-opt1-variable
그것이 존재하거나 $(reboot)
포함 $2
된다면, '; rm -rf ~ #
당신은 비참한 결과를 상상할 수 있습니다.
zsh에서는 eval " $cmd"
배열 요소 사이의 첫 번째 문자를 사용하여 배열 요소를 연결하는 것이 짧습니다. 공백으로 시작하는 기본값에는 문제가 없지만 수정된 컨텍스트에서 사용하면 중단됩니다(명시적인 분할 및 연결 연산자가 있는 zsh에서는 일반적이지 않으므로 수정이 거의 필요하지 않습니다).eval " ${cmd[*]}"
$IFS
$IFS
$IFS
$IFS
여기서 목적이 명령을 여러 줄로 나누어 읽기 쉽게 만드는 것이라면 거의 모든 셸에서 작동하는 전통적인 접근 방식은 다음을 사용하는 것입니다.줄 연속줄 끝에 백슬래시를 삽입하면 다음과 같습니다.
my_function() {
main_cmd hard_coded_arg_1 hard_coded_arg_2 \
-a "$opt1" \
-b hard_coded_opt \
"$2"
}
연속된 행은 일반적으로 별도의 행이 아니라 연속된 행임을 더 명확하게 하기 위해 들여쓰기됩니다. 어떤 사람들은 다음과 같이 작성하는 것을 선호합니다.
my_function() {
main_cmd hard_coded_arg_1 hard_coded_arg_2 \
-a "$opt1" \
-b hard_coded_opt \
"$2"
}
\
이렇게 하면 a가 누락된 경우를 더 쉽게 감지할 수 있습니다 .
어쨌든 여기에서는 별칭을 사용하고 싶지 않을 것입니다. 별칭은 매우 초기에 확장된다는 점에서 C 전처리기 매크로와 약간 유사합니다(C의 전처리 단계에서 셸에서 읽고 코드를 표시하고 해당 구문을 부분적으로 구문 분석한 후). 그러나 C와는 달리 다음과 같은 경우에만 확장된 후에만 가능합니다.명령 위치² 그리고 논쟁을 받아들이지 마십시오.
array=( alias_name ... )
이는 명령 위치에서 찾을 수 없기 때문에 확장되지 않는 이유를 설명합니다 .alias_name
일부 조건에서 코드로 재평가될 일부 텍스트로 대체되는 토큰이 아니라 실제로 실제 명령을 정의하기 때문에 main_cmd() the command "$@"
대체를 사용하는 데 문제가 없습니다.alias main_cmd='the command'
main_cmd
1 별칭 확장 후 결과의 구문이 다시 분석되어 명령의 의미가 완전히 바뀔 수도 있고 추가 별칭 확장 라운드가 진행될 수도 있습니다.
² alias -g
전역 zsh
별칭이 표시되지만 이러한 별칭은 다음 영역뿐만 아니라 전역으로 확장됩니다.명령 위치