$PATH
bash/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
내부에 if
s가 있는 루프이며, 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]}
함수와 별칭을 포함하려면 $functions
and 를 사용할 수 있지만 $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
}
그러나 나는 당신의 버전이 좋고 읽기 쉽다고 생각합니다. 나는 그 길이에 대해 걱정하지 않을 것입니다.
변수 할당에는 왼쪽에 달러 기호가 있을 수 없으며 type
PATH의 실행 파일 외에 함수와 별칭도 검색됩니다.
답변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#*:}
$PATH
while
"${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
다음 zsh
과 bash
같은 정의를 작성합니다 yash
.
alias x='something or other'
...그냥 ksh93
...
something or other
……포함 dash
……
x='something or other'
...하지만 다시 말하지만, 모두가 변수를 얻습니다.$type
배포.
매개변수를 전달하는 경우예쉘 별칭이며 이름에 가 포함되어 있어도 어떤 방식으로든 정의되어 있으므로 =
찾을 수 없습니다. 해당 기능이 필요한 경우 alias
반환을 테스트하는 대신 grep의 출력이 필요합니다.
실행 파일이 발견되면 command -V
함수가 반환되기 전에 출력이 표준 오류에 기록됩니다. 실행 파일을 찾을 수 없으면 false를 반환합니다.