zsh 명령에서 경로 끝의 루트를 참조하는 방법은 무엇입니까?

zsh 명령에서 경로 끝의 루트를 참조하는 방법은 무엇입니까?

나는 종종 git 저장소를 복제한 다음 해당 루트 디렉터리로 이동합니다. 예를 들어:

$ git clone https://github.com/hpjansson/chafa && cd chafa

cc이를 좀 더 쉽게 만들기 위해 다음과 같이 확장되는 zsh 약어가 있습니다 && cd !#:2:t.

typeset -Ag abbrev
abbrev=(
  'G' '| grep -v grep | grep'
  'L' '2>&1 | less'
  'V' '2>&1 | vipe >/dev/null'
  'ac' '| column -t'
  'bl' '; tput bel'
  'cc' '&& cd !#:2:t'
  'fl' "| awk '{ print $"
  'ne' '2>/dev/null'
  'pf' "printf -- '"
  'sl' '>/dev/null 2>&1'
  'tl' '| tail -20'
)
__abbrev_expand() {
  emulate -L zsh
  setopt EXTENDED_GLOB
  local MATCH
  LBUFFER=${LBUFFER%%(#m)[a-zA-Z]#}
  if [[ "${LBUFFER: -1}" == ' ' ]]; then
    LBUFFER+=${abbrev[$MATCH]:-$MATCH}
    if [[ $MATCH = 'fl' ]]; then
      RBUFFER="}'"
    elif [[ $MATCH = 'pf' ]]; then
      RBUFFER="\n'"
    fi
  else
    LBUFFER+=$MATCH
  fi
  zle self-insert
}
zle -N __abbrev_expand
bindkey ' ' __abbrev_expand
bindkey -M isearch ' ' self-insert

!#man zshexpn(섹션 HISTORY EXPANSION, 하위 섹션 Event Designators) 에 설명된 대로 현재 명령줄을 참조합니다 .

!# 지금까지 입력된 현재 명령줄을 확인하세요. 줄은 따옴표가 있는 줄 앞의 단어까지 포함하여 완전한 것으로 간주됩니다 !#.

:2명령줄의 두 번째 단어를 참조합니다(명령어를 입력할 때의 URL입니다 $ git clone).

n n번째 매개변수입니다.

그리고 :t단어의 끝을 나타냅니다.

t 모든 선행 경로 이름 구성 요소를 제거하고 후행 구성 요소는 그대로 둡니다. 마치 basename.


일반적으로 git clone명령줄에 입력한 다음 프로젝트의 URL을 복사하여 붙여넣고 공백을 삽입한 다음 cc또 다른 공백을 삽입하면 원하는 명령이 제공됩니다.

하지만 복사하여 붙여넣은 URL이 확장자로 끝나는 경우가 있습니다 .git. 이런 일이 발생하면 확장자에 !#:2:t원치 않는 확장자가 포함됩니다 .git.

$ git clone https://github.com/hpjansson/chafa.git cc
$ git clone https://github.com/hpjansson/chafa.git && cd !#:2:t
$ git clone https://github.com/hpjansson/chafa.git && cd chafa.git
cd: no such file or directory: chafa.git

:r한 가지 해결책은 수정자를 사용하여 .git확장을 제거하는 것입니다.

r 파일 확장자를 제거하고 루트 이름을 유지하십시오. 파일 확장자가 없는 문자열은 변경되지 않습니다. 파일 확장자 뒤에는 둘 다 아닌 .임의 개수의 문자(0 포함)가 오고 문자열 끝까지 계속됩니다. 예를 들어, 확장자는 is 이고 확장자가 없습니다../foo.orig.c.cdir.c/foo

                                                               vv
$ git clone https://github.com/hpjansson/chafa.git && cd !#:2:t:r
$ git clone https://github.com/hpjansson/chafa.git && cd chafa

.git그러나 경로가 확장으로 끝나지 않으면 확장이 실패하고 git명령이나 cd명령이 모두 실행되지 않습니다.

다음은 문제를 설명하는 최소한의 예입니다.

$ echo /foo/bar.baz !#:1:t:r
/foo/bar.baz bar

$ echo /foo/bar !#:1:t:r
zsh: modifier failed: r

첫 번째 명령은 마지막 경로 구성 요소에 확장자가 포함되어 있기 때문에 성공 .baz하지만 두 번째 명령은 더 이상 확장자가 없기 때문에 실패합니다. OTOH는 bash에서 4.3.48두 명령 모두 예상대로 작동합니다.

:r문서에 다음 문장이 포함되어 있기 때문에 확장 없이는 왜 실패하는지 이해할 수 없습니다 .

파일 확장자가 없는 문자열은 변경되지 않습니다.

:r확장자 없이 문자열을 사용하는 것이 잘못되었다는 의미는 아닙니다 .


확장자를 제거하기 위해 수정자를 사용하는 또 다른 접근 방식을 시도했습니다 :s. 내 생각에는 다음과 HIST_SUBST_PATTERN같은 옵션 설정이 필요하다고 생각합니다.

setopt HIST_SUBST_PATTERN

이 옵션을 사용하면 확장자를 제거하기 위해 쓸 수 있습니다 :s/.*.

                          vvvvv
$ echo /foo/bar.baz !#:1:t:s/.*
/foo/bar.baz bar

확장이 있으면 작동하지만 확장이 없으면 다시 실패합니다.

$ setopt HIST_SUBST_PATTERN
$ echo /foo/bar !#:1:t:s/.*
zsh: substitution failed

bar/foo/bar.bazin 과 to in 을 모두 참조할 수 bar있는 단일 수정자 시퀀스가 ​​있습니까 /foo/bar?

나는 그것을 사용하고 있습니다 zsh 5.7.1-dev-0 (x86_64-pc-linux-gnu).

답변1

기록 확장을 통해 이를 수행할 수 있는 방법이 생각나지 않습니다. 그러나 나는 히스토리 확장이 최고의 도구라고 생각하지 않습니다. 이것은 매우 제한적입니다. 에서링글 위젯를 사용하면 명령줄에 액세스하여 임의의 코드로 작업할 수 있습니다. 이것을 활용하십시오.

한 가지 방법은 약어를 확장하는 것입니다. $abbrev[$MATCH]대체를 사용하십시오 ${(e)abbrev[$MATCH]}. 분명히 특수 문자를 올바르게 참조하려면 약어를 변경해야 합니다. 의 경우 cc다음과 같은 것을 사용하십시오.

&& cd ${${(z)BUFFER}[3]}

의 특정 경우 git clone및 보다 일반적으로 현재 디렉토리 내에 디렉토리를 생성하는 모든 경우에는 다른 약어를 사용할 수 있습니다. 새로 생성된 디렉토리로 전환합니다. git clone https://example.com/foo.git, for git clone https://example.com/foo및 for git clone https://example.com/foo.git bar뿐만 아니라 for tar xf foo.tar(아카이브에 최상위 디렉토리가 있는 경우) 및 에서도 작동합니다 mv /some/directory ..

&& cd *(/oc[1])

관련 정보