bash가 zsh 코드를 구문 분석하지 못하도록 하는 방법이 있습니까?

bash가 zsh 코드를 구문 분석하지 못하도록 하는 방법이 있습니까?

일부 기능을 포함 .bashrc하고 있는 파일이 있습니다 . .zshrc이전에는 각 셸에 대해 두 개의 별도 파일이었지만 지금은 파일이 거의 동일하고 단일 함수 파일을 관리하는 것이 더 쉽기 때문에 단일 파일을 사용합니다.

두 쉘 모두 서로 다른 기능이 있으므로 올바른 명령문이 사용되도록 조건문을 사용할 수 있다고 생각했습니다.

[[ $BASH_VERSION ]] && shell=bash
[[ $ZSH_VERSION ]] && shell=zsh

# Search man page for string
if [[ $shell == bash ]]; then
  mans() {
    local q="\'"
    local q_pattern="'${1//$q/$q\\$q$q}'"
    local MANPAGER="less -+MFX -p $q_pattern"
    man "$2"
  }
elif [[ $shell == zsh ]]; then
  mans() MANPAGER="less -+MFX -p ${(q+)1}" man $2
fi

이것은 zsh에서는 잘 작동하는 것 같지만 bash로 전환하면 bash에서 사용해서는 안 되지만 zsh 라인에서 오류가 발생합니다. 해당 줄을 구문 분석하는 것을 막을 수 있는 방법이 있나요?

답변1

ifbash예를 들어 다음과 같은 일부 , ifzsh, 별칭을 정의할 수 있습니다 endif.

alias if{ba,z}sh=':||:<<"endif"' endif=
if [[ -n $BASH_VERSION ]]; then
  alias ifbash=
  shopt -s expand_aliases
elif [[ -n $ZSH_VERSION ]]; then
  alias ifzsh=
fi

ifbash
f() { echo bash; }
endif

ifzsh
f() echo ${(q+):-zsh}
endif

:||:<<"MARKER"...은 MARKER코드 섹션을 주석 처리하는 관용적인 방법입니다. 이는 stdin에서 here-doc를 사용하여 내장된 noop를 :<<"MARKER"실행하는 것과 비슷하지만 추가로 두 번째 문서는 실행되지 않고 here-document도 생성되지 않고 구문 분석만 됩니다.::||:

들여쓰기를 해서 endif도 안 되고, 뒤에 아무것도 추가해서도 안 됩니다. 전체 줄을 구성해야 합니다. ifbash/ ifzshs는 중첩될 수 없습니다(어쨌든 여기서는 의미가 없습니다).

그런데, 인용 구문 분석은 환경 변수에서 별도로 수행되는 것으로 man보이며 MANPAGER(쉘을 호출하는 대신 ) 인용 형식을 $SHELL지원하지 않는 것 같으 므로 일부 문자열의 매개변수 확장 플래그를 사용하면 해당 형식을 사용할 수 있습니다. 인용문이 잘못되었습니다. 대신 bash 버전에서처럼 작은 따옴표를 사용하고 따옴표 외부에서 작은 따옴표를 이스케이프 처리하는 방법을 사용하세요 .$'...'q+qq\'


¹ rcquotes이 옵션을 활성화 하지 않으면 ''à la가 대신 사용됩니다. 함수 내에서 합리적인 로컬 옵션 세트를 갖는 것을 함수 시작 부분에 rc추가하면 emulate -L zsh함수가 실행되지 않고 정의될 때 옵션을 비활성화해야 하기 때문에 도움이 되지 않습니다.

답변2

배쉬는 여전히 필요하다분석하다모든 줄을 실행하지는 않더라도 전체 파일입니다. 넌 보통 숨길 수 없어통사론다음 오류는 조건부 오류이며 런타임 오류일 뿐입니다.

당신 대신에 나는 별도의 .zshrc.bashrc. 그런 다음 일반적으로 사용되는 기능을제삼파일(파일이 두 가지 모두와 호환되는 구문만 사용하는지 확인) 및 에서 가져 .zshrc옵니다 .bashrc.

제 생각에는 이것이 세계 최고입니다.

  1. zsh 전용 기능은 .zshrcbash가 절대 읽을 수 없는 위치로 이동합니다. 마찬가지로 bash 전용 콘텐츠는 .bashrc.
  2. 범용 함수에는 하나의 정의가 있으므로 두 개의 복사본을 유지할 필요가 없습니다(명시적으로 크로스 쉘인 파일에 있으므로 두 복사본과의 호환성을 유지하는 것을 잊어버릴 가능성이 적습니다).
  3. 각 셸에는 정규화된 이름이 포함된 자체 시작 파일이 있어 이를 구성할 위치가 더 명확하며, 각 셸에는 공용 파일도 사용한다고 명시적으로 명시되어 있습니다.
  4. 쉘 문자열 내에서 쉘 구문을 유지할 필요가 없습니다(예제보다 인용이 더 복잡하거나 구문 강조 표시가 손실될 때 문제가 됩니다).

답변3

다른 이유가 없다면 모든 쉘은 조건이 끝나는 부분을 찾기 위해 조건의 코드를 구문 분석해야 합니다.

쉘 특정 코드를 다른 파일이나 문자열에 넣는 대신 evalhere-doc를 사용하여 source숨기고 here-doc를 직접 숨길 수 있습니다. 를 사용하는 것과 거의 동일 eval하지만 here-doc의 장점은 내부 코드에 성가신 이스케이프 문제 없이 작은따옴표와 큰따옴표를 포함할 수 있다는 것입니다.

예를 들어, 이 스크립트는 hello, 'bar', I'm zshzsh로 실행하면 Bash에서 오류가 발생한다고 말합니다.

if [[ -n $ZSH_VERSION ]]; then
    source /dev/stdin <<'EOF'
foo() {
   echo "hello, '$1', I'm zsh"
}
EOF
fi

foo bar

그러나 이것이 source /dev/stdin모든 쉘과 시스템에서 작동하는지 확실하지 않습니다. (예, source이것은 내장 명령의 비표준 이름이지만 .Bash 및 zsh에서 작동해야 하며 명령은 어쨌든 실행 조건 내의 셸에만 존재하면 됩니다.)

답변4

방법@ilkkachu가 제안한 것일하다:

# Search man page for string
if [[ $shell == bash ]]; then
  mans() {
    local q="\'"
    local q_pattern="'${1//$q/$q\\$q$q}'"
    local MANPAGER="less -+MFX -p $q_pattern"
    man "$2"
  }
elif [[ $shell == zsh ]]; then
  mansfunc='mans() MANPAGER="less -+MFX -p ${(q+)1}" man $2'
  eval "$mansfunc"
fi

관련 정보