별칭 명령이 있는 함수는 eval과 함께 사용할 수 있지만 셸 확장에는 사용할 수 없습니다.

별칭 명령이 있는 함수는 eval과 함께 사용할 수 있지만 셸 확장에는 사용할 수 없습니다.

아래는 내가 실행하려는 기능입니다. 문제는 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[@]}"

다음 요소를 포함하는 배열을 정의합니다.

  1. main_cmd
  2. hard_coded_arg_1
  3. hard_coded_arg_2
  4. -a
  5. value-of-opt1-variable
  6. -b
  7. hard_coded_opt
  8. 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[@]}"

어레이에는 다음이 포함됩니다.

  1. main_cmd hard_coded_arg_1 hard_coded_arg_2
  2. -a "$opt1"
  3. -b hard_coded_opt
  4. "$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[@]"

당신이 얻는 배열의 요소는 다음과 같기 때문에 잘못되었습니다.

  1. main_cmd
  2. hard_coded_arg_1
  3. hard_coded_arg_2
  4. -a
  5. value-of-opt1-variable
  6. -b
  7. hard_coded_opt
  8. '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별칭이 표시되지만 이러한 별칭은 다음 영역뿐만 아니라 전역으로 확장됩니다.명령 위치

관련 정보