쉘: 사용 가능한 프로그램 선택

쉘: 사용 가능한 프로그램 선택

$PATHbash/ksh/zsh에서 찾을 수 있는(또는 쉘에서 호출할 수 있는) 대안 목록의 첫 번째 변수를 설정하는 좋은 관용구가 있습니까?

예를 들어 작동해야 하는 스크립트가 있는 경우실제 경로(1)아래에 설치 realpath하거나 grealpath먼 길을 갈 수 있습니다:

 if type "grealpath" > /dev/null; then
    realpath_exec=grealpath
  elif type "realpath" > /dev/null; then
    realpath_exec=realpath
  else
    echo "$0: No realpath found in PATH" >&2
    exit 1
  fi

표현식을 작성하기 위해 내가 생각할 수 있는 ||대부분의 방법에는 &&중첩된 하위 쉘(큰 문제는 아니며, 자주 호출되는 함수의 성능에 좋지 않고 귀찮은 일임) 및/또는 grep다양한 출력을 처리하기 위한 복잡한 리디렉션 또는 추악함이 필요합니다. (참고하세요,산출설치된 "프로그램"이 함수 또는 별칭인 경우 type직접 사용할 수 있습니다. which따라서 작업을 수행할 무언가를 호출할 수 있는지 가정하려면 realpath출력 또는 별칭 대신 반환 값을 켜야 합니다. grep을 해보세요. )

위의 내용은 훌륭합니다. 그러한 프로그램에 대해 여러 옵션이 있는 경우 시간이 많이 걸리고 고통스럽습니다. 더 있나요?우아한목록에서 사용 가능한 첫 번째 프로그램을 선택하고 이를 변수에 할당할 수 있습니까?

답변1

가장 간단하고 이해하기 쉬운 것은 for내부에 ifs가 있는 루프이며, break일치하는 항목을 찾으면 루프에서 빠져 나옵니다.


이 작업을 원하지 않으면 Bash에서type여러 매개변수를 허용합니다.찾다:

type grealpath realpath ...

일치하는 항목의 출력 줄의 첫 번째 단어는 항상 1 명령/함수/별칭의 이름입니다. 추가 하위 프로세스를 피하기 위해 배열의 stdout을 캡처하고 색인화할 수 있습니다.

words=($(type realpath grealpath foo make 2>/dev/null))
realpath_exec=${words[0]}
if ! [ "$realpath_exec" ] ...

이것은 어떤 관점에서는 우아하지만, 우연히 발견하면 분명히 이해하기가 더 어렵습니다.


배열 이니셜라이저의 토큰화에 의존한다의 가치IFS. IFS이것이 변경되었거나 명령에 공백이 포함되어 있으면 작동하지 않습니다 .


1 실험에 따르면, 이렇습니다.최대로케일은 자연스러운 단어 순서가 아니더라도 항상 U+0020 ASCII 공백이 뒤따르지만 출력 형식은 더 이상 지정되지 않습니다. 일부 지역화에서는 이것이 작동하지 않습니다. 이것이 문제를 일으킬지 여부를 고려해야 합니다. LC_ALL=C type ...(다소) 적합하다고 보장되는 출력 형식을 사용할 수 있습니다 .


zsh에서, 함수나 별칭이 아닌 실행 파일에만 관심이 있다면 다음을 사용할 수 있습니다.$commands:

realpath_exec=${commands[grealpath]:-${commands[realpath]:-${commands[another]:?No compatible command found}}}

확장은 null이 아닌 경우 값을 제공하고 ${param:-...}, 그렇지 않으면 오류가 발생합니다. 이것은 여전히 ​​​​매우 추악합니다.param...${param:?...}...param

명령 선택 사이의 순서에 신경 쓰지 않는다면 더 간단한 버전을 사용할 수 있습니다(i) 아래첨자 표시:

realpath_exec=${commands[(i)realpath|grealpath|another]}

함수와 별칭을 포함하려면 $functionsand 를 사용할 수 있지만 $aliases반복됩니다.

답변2

이를 다음과 같이 리팩터링할 수 있습니다.

isExecutable(){ type "$1" >/dev/null 2>&1 && printf "%s\n" "$1"; }

realpath_exe=`isExecutable grealpath || isExecutable realpath`  
[ -n "$realpath_exe" ] || {
   echo "$0: No realpath found in PATH" >&2
   exit 1
}

그러나 나는 당신의 버전이 좋고 읽기 쉽다고 생각합니다. 나는 그 길이에 대해 걱정하지 않을 것입니다.

변수 할당에는 왼쪽에 달러 기호가 있을 수 없으며 typePATH의 실행 파일 외에 함수와 별칭도 검색됩니다.

답변3

type에 대한 귀하의 출력은 정확하므로 which사용해서는 안됩니다. 을 사용해야 합니다 command.

pathx()
    for   cmd
    do    set "" "${PATH:?is NULL!}:"
          while  "${2:+set}" -- "${2%%:*}" "${2#*:}" 2>&3
          do      command -v -- "${1:-.}/$cmd"
    done; done    3>/dev/null

command -v $PATH_component/$cmd이는 별칭이나 내장 함수 또는 기타 항목을 나열하지 않기 위해 이 작업만 수행합니다. $PATH각 인수에 대해 환경 변수의 각 구성 요소를 재귀적으로 검색하고 찾은 내용을 표준 출력에 인쇄합니다.

나중에 추가 하면 && break해당 인수 중 하나에 의해 명명된 'd 실행 파일을 처음 성공적으로 찾을 때 검색이 중단됩니다 command -v ....$PATH$PATH

그것은 마이클 호머의 아이디어였고, 정말 최고의 아이디어였습니다.

while루프 내에 루프를 중첩하여 작동합니다 for. 루프 for가 반복될 때마다 for루프 while는 루프의 각 구성 요소를 반복하고 콜론으로 구분된 $PATH가장 짧은 문자열을 테스트하고 다음 반복을 위해 가장 긴 문자열을 저장합니다. :루프가 문자열 실행을 시도하고 실패하는 경우에만 루프가 완료되고 다음 반복이 시작됩니다.${2%%:*}command -v $slice/$cmd${2#*:}$PATHwhile"${2:-NUL}"for

cp /bin/cat /tmp
(PATH=$PATH:/tmp pathx cat dd dummy doesntexist read echo)

/usr/bin/cat
/tmp/cat
/usr/bin/dd
/usr/bin/echo

분명히 당신은 뭔가를 원합니다 aliases. 음, 이것은 작동합니다:

shellx()
    for  cmd
    do  "set"  --   "$cmd";"unset" cmd
         for   type  in     alias  exe
         do    case  $type  in
               (a*)        "alias"  "${1%%*=*}"     ;;
               (e*)  PATH= "command" -v -- "$1"     &&
                            type=function::builtin  ||
                           "command" -v -- "$1"     ;;
               esac  >&2&& "command" -V -- "$1" >&3 &&
                     cmd=$("command" -v -- "$1")    && return
    done;done  3>&2 2>/dev/null

이것은 제가 기억하는 것보다 어렵지만 실행 가능한 매개변수 중 하나를 찾으면 종료됩니다. 그것은 유형을 넣습니다 - 하나별명,기능::내장, 또는exe존재하다$type, 그런 다음 명령이 입력됩니다.$cmd. Alias는 $cmd다음 zshbash같은 정의를 작성합니다 yash.

alias x='something or other'

...그냥 ksh93...

something or other

……포함 dash……

x='something or other'

...하지만 다시 말하지만, 모두가 변수를 얻습니다.$type배포.

매개변수를 전달하는 경우쉘 별칭이며 이름에 가 포함되어 있어도 어떤 방식으로든 정의되어 있으므로 =찾을 수 없습니다. 해당 기능이 필요한 경우 alias반환을 테스트하는 대신 grep의 출력이 필요합니다.

실행 파일이 발견되면 command -V함수가 반환되기 전에 출력이 표준 오류에 기록됩니다. 실행 파일을 찾을 수 없으면 false를 반환합니다.

관련 정보