스크립트에서 glob을 사용하여 하위 디렉터리가 있는지 확인

스크립트에서 glob을 사용하여 하위 디렉터리가 있는지 확인

bin때로는 변경될 수 있는 디렉토리 안에 디렉토리가 있는지 확인하려고 합니다 . 이 특별한 경우에는 Ruby의 버전 번호가 변경될 수 있습니다(예 $HOME/.gem/ruby/2.6.0/bin: ). 지금까지 내가 한 일은 다음과 같습니다.

#!/usr/bin/env zsh
ruby_gem_home="$HOME/.gem/ruby/*/bin"
if [[ -d $ruby_gem_home ]]; then
  echo "The ruby gems directory exists!"
else
  echo "Ruby gems directory missing!"
fi

find이것은 로그인 프로세스의 일부이므로 사용하고 싶지 않습니다 . 이를 달성하기 위해 내장된 zsh/bash 명령을 사용하는 가장 우아한 방법은 무엇입니까?

감사해요!

편집하다:이것이 zsh스크립트를 위한 것임을 언급하는 것을 잊었습니다.

답변1

이 경우에는 구체적으로

디렉토리를 찾는 것은 의미가 없다고 생각합니다. 설치된 Ruby 버전과 일치하지 않는 디렉토리를 사용하는 이유는 무엇입니까? 그리고 사용자가 에서 다른 디렉토리를 구성했다면 어떻게 될까요 ~/.gemrc?

저는 Ruby에 그다지 익숙하지 않지만 올바른 해결책은 gempath.

ruby_gem_home=$(gem environment gempath | sed 's/:.*//')
if [ -z "$ruby_gem_home" ]; then unset ruby_gem_home; fi

zsh에서

function set_ruby_gem_home {
  emulate -LR zsh
  local dirs
  dirs=($HOME/.gem/ruby/*/bin(NnOn))
  if (($#dirs == 0)); then
    unset ruby_gem_home
  else
    ruby_gem_home=$dirs[0]
  fi
}

그만큼글로벌 한정자 N일치하는 항목이 없는 경우 결과가 오류가 아닌 빈 배열인지 확인합니다. glob 한정자는 n버전 On번호를 줄여 목록을 정렬합니다. 일치하는 항목이 여러 개 있을 때 다른 항목을 선택하려면 이를 조정하세요.

배쉬에서:

function set_ruby_gem_home {
  local dirs
  dirs=(~/.gem/ruby/*/bin(NnOn))
  if (($#dirs == 0)); then
    unset ruby_gem_home
  else
    ruby_gem_home=$dirs[0]
  fi
}

배쉬에서

function set_ruby_gem_home {
  local dirs restore_options
  restore_options=$(setopt +o)
  shopt -s nullglob
  dirs=(~/.gem/ruby/*/bin)
  if (($#dirs == 0)); then
    unset ruby_gem_home
  else
    ruby_gem_home=${dirs[${#dirs[@]}-1]}
  fi
  eval "$restore_options"
}

여기서는 항상 최신 버전이 아닌 경우가 많은 마지막 배열 요소를 사용합니다. 10is sorted before 9.

답변2

배열을 사용하고 배열의 첫 번째 요소(예: bash의 경우 [0], zsh의 경우 [1])가 디렉터리인지 확인하세요. 예 bash:

# the trailing slash in "$HOME"/.gem/ruby/*/bin/ ensures that
# the glob matches only directories.
rubygemdirs=( "$HOME"/.gem/ruby/*/bin/ )

if [ -d "${rubygemdirs[0]}" ] ; then
   echo "At least one ruby gem dir exists"
else
   echo "No ruby gem dirs were found"
fi

zsh및 에서 작업하려면 bash먼저 현재 실행 중인 셸을 찾아야 합니다. 그러면 배열의 첫 번째 인덱스가 0또는 인지 알 수 있습니다 1. 또한 (N)glob과 일치하는 항목이 없는 경우 zsh에 종료하지 말라고 지시하기 위해 glob과 함께 사용해야 하는지 여부도 결정합니다 (zsh는 glob 일치 오류 시 기본적으로 종료됩니다).

(이 시점에서 안정적으로 작동하는 스크립트를 작성하는 것이 분명해지기 시작해야 합니다.둘 다bash와 zsh는 아마도 가치 있는 것보다 더 많은 문제가 될 것입니다)

if [ -n "$BASH_VERSION" ] ; then
   first_idx=0
   rubygemdirs=( "$HOME"/.gem/ruby/*/bin/ )
elif [ -n "$ZSH_VERSION"  ] ; then
   first_idx=1
   emulate sh
   rubygemdirs=( "$HOME"/.gem/ruby/*/bin/ )
   emulate zsh
fi

if [ -d "${rubygemdirs[$first_idx]}" ] ; then
...

필요한 경우 배열을 반복하여 요소가 특정 버전과 일치하는지 확인할 수도 있습니다. 예를 들어

for d in "${rubygemdirs[@]}" ; do
  [[ $d =~ /2\.6\.0/ ]] && echo found gem dir for ruby 2.6.0
done

참고: /정규식의 s는 문자 그대로 슬래시입니다. 에서처럼 정규식의 시작과 끝을 표시하지 않습니다 sed.

답변3

와일드카드는 목록 컨텍스트에서만 확장됩니다. 즉, 여러 항목으로 확장될 수 있습니다. 스칼라 변수에 대한 할당에서는 스칼라 변수가 하나의 값만 보유할 수 있으므로 확장할 수 없습니다.

를 사용하면 목록 컨텍스트에서 glob이 확장할 파일 목록이 아니라 문자열을 스칼라 변수에 ruby_gem_home="$HOME/.gem/ruby/*/bin"저장하게 됩니다 ./home/user/.gem/ruby/*/bin$ruby_gem_home

for/select x inzsh의 목록 컨텍스트는 리디렉션 대상( multios켜져 있는 경우), 배열 또는 연관 배열 할당 및 기타 몇 가지 특수한 경우1 에서 간단한 명령 또는 익명 함수에 대한 인수가 될 수 있습니다 .

따라서 여기서는 익명 함수를 사용할 수 있습니다.

() {
  unset ruby_gem_home
  case $# in
    (0) print -u2 "Ruby gems directory missing!";;
    (1) print -u2 "The ruby gems directory exists!"
        ruby_gem_home=$1;;
    (*) print -u2 "More than one ruby gem directory, don't know which to choose!";;
  esac
} ~/.gem/ruby/*/bin(N-/)

여기에는 N오류를 보고하는 대신 일치하는 항목이 없을 때 glob이 빈 목록으로 확장되고 -/유형의 파일로 제한되도록 glob 한정자를 추가합니다.예배 규칙서(를 사용한 심볼릭 링크 해결 후에 결정됨 -).

Ruby gem 디렉터리가 두 개 이상 있고 버전 번호가 가장 높은 디렉터리를 선택하려면 다음을 수행하세요.

() {
  unset ruby_gem_home
  case $# in
    (0) print -u2 "Ruby gems directory missing!";;
    (1) print -u2 "The ruby gems directory exists!"
        ruby_gem_home=$1;;
    (*) print -ru2 "More than one ruby gem directory, picking $1!"
        ruby_gem_home=$1;;
  esac
} ~/.gem/ruby/*/bin(N-/nOn)

n켜지고 rder가 ame 에 의해 역방향으로 바뀌 numericglobsort므로 가장 높은 숫자를 가진 항목이 포함됩니다.On on$1


¹ 설명대로info zsh 'Conditional Expressions', 옵션이 켜져 있는 특별한 경우도 있는데, 이 경우 일반적으로 / 연산자와 함께 extendedglob내부 glob을 사용할 수 있습니다 . 따라서 여기에 그러한 디렉토리가 하나 이상 있는지 확인하는 데 사용할 수 있습니다 .[[ ... ]]-n-z[[ -n ~/.gem/ruby/*/bin(#qN-/) ]]

관련 정보