" which "를 사용하지 않는 이유는 무엇입니까? 그러면 무엇을 사용해야 합니까?

" which "를 사용하지 않는 이유는 무엇입니까? 그러면 무엇을 사용해야 합니까?

실행 파일의 경로를 찾거나 Unix 셸에 명령 이름을 입력하면 어떤 일이 발생하는지 확인할 때 다양한 유틸리티( which, type, command, whence, where, 등 whereis) 가 있습니다 .whatishash

which우리는 이것을 피해야 한다는 말을 자주 듣습니다 . 왜? 대신 무엇을 사용해야 할까요?

답변1

당신이 알고 싶지 않다고 생각한 모든 것은 다음과 같습니다.

일반화하다

Bourne과 유사한 쉘 스크립트에서 실행 파일의 경로 이름을 얻으려면(몇 가지 주의 사항이 있음, 아래 참조):

ls=$(command -v ls)

특정 명령이 존재하는지 확인하려면 다음을 수행하십시오.

if command -v given-command > /dev/null; then
  echo given-command is available
else
  echo given-command is not available
fi

Bourne과 같은 대화형 쉘의 프롬프트에서:

type ls

which명령은 C-Shell의 파괴적인 유산이며 Bourne과 같은 쉘에 단독으로 두는 것이 가장 좋습니다.

이 정보를 스크립트의 일부로 찾는 것과 쉘 프롬프트에서 대화형으로 찾는 것에는 차이가 있습니다.

쉘 프롬프트에서 일반적인 사용 사례는 다음과 같습니다.이 명령은 이상하게 작동합니다. 올바른 명령을 사용하고 있습니까? 내가 이것을 입력하고 있는 동안 도대체 무슨 일이 일어나고 있는 걸까 mycmd? 어떤 내용인지 좀 더 자세히 살펴봐도 될까요?

이 경우 실제로 명령을 호출하는 것보다 명령이 호출될 때 쉘이 수행하는 작업을 알고 싶습니다.

쉘 스크립트에서는 상당히 다른 경향이 있습니다. 쉘 스크립트에서 단순히 명령을 실행하려는 경우 명령이 어디에 있는지, 무엇인지 알고 싶을 이유가 없습니다. 일반적으로 알고 싶은 것은 실행 파일의 경로이므로 해당 파일에서 더 많은 정보를 얻을 수 있습니다(예: 해당 파일과 관련된 다른 파일의 경로 또는 해당 경로 정보에서 실행 파일의 내용을 읽을 수 있음).

대화형 방식으로 다음 사항을 알고 싶을 수도 있습니다.모두my-cmd이와 같이 시스템(스크립트)에서 사용할 수 있는 명령은 거의 없습니다.

사용 가능한 대부분의 도구(종종 그러한 경우임)는 대화형으로 사용하도록 설계되었습니다.

역사

첫째, 약간의 역사입니다.

1970년대 후반까지 초기 Unix 셸에는 기능이나 별칭이 없었습니다. 별칭은 1978년경 에야 도입되었습니다(처음으로 소개되었지만 $PATH) .cshcsh해방됨, 1979 년 2BSD5월) .cshrc사용자 정의 쉘에 대해서도 처리됩니다(예를 들어 각 쉘은 csh스크립트 .cshrc와 같이 대화형이 아닐 때도 읽습니다).

rcBourne 쉘은 1979년 초 Unix V7에서 처음 출시되었지만 기능 지원은 훨씬 나중에 추가되었으며(1984년 SVR2에서) 어떤 경우에도 일부 파일이 없었습니다 ( .profile쉘이 아닌 환경 구성을 위해)그 자체).

csh대화식 사용에 더 편리하고 더 나은 많은 기능을 추가하기 때문에 Bourne 쉘보다 더 인기가 있습니다(구문은 Bourne 쉘보다 훨씬 나쁩니다).

3BSD(1980) 에서는whichcsh 스크립트csh사용자가 실행 파일을 식별하는 데 도움을 주기 위해 추가된 이 which스크립트는 오늘날 많은 상용 Unices(Solaris, HP/UX, AIX 또는 Tru64 등)에서 찾을 수 있는 스크립트와 거의 다르지 않습니다.

스크립트는 사용자의 명령 ~/.cshrc(호출을 사용하지 않는 한 모든 스크립트와 마찬가지로 )을 읽고 csh별칭 목록 및 (유지되는 배열을 기반으로) csh -f제공된 명령 이름을 찾습니다 .$pathcsh$PATH

여기 있습니다: which첫 번째는 당시 가장 인기 있는 쉘이었고( csh90년대 중반까지 여전히 인기가 있었습니다) 이것이 책에 기록되어 여전히 널리 사용되는 주된 이유입니다.

csh사용자 의 경우에도 whichcsh 스크립트가 반드시 올바른 정보를 제공하는 것은 아닙니다. ~/.cshrc나중에 프롬프트에서 정의하거나 source다른 파일을 지정 하여 별칭을 가져오는 것이 아니라 에 정의된 별칭을 가져오며 csh(좋은 생각은 아니지만) PATH에서 다시 정의될 수 있습니다 ~/.cshrc.

whichBourne 셸에서 명령을 실행하면 에 정의된 별칭을 계속 찾을 수 있지만 ~/.cshrc, 별칭을 사용하지 않아 별칭이 없는 경우 csh에도 올바른 답을 얻을 수 있습니다.

1984년이 되어서야 내장 명령을 통해 SVR2의 Bourne 쉘에 유사한 기능이 추가되었습니다 type. (외부 스크립트가 아닌) 내장되어 있다는 사실은할 수 있는셸 내부에 액세스할 수 있으므로 올바른 정보(어느 정도)를 제공합니다.

초기 명령은 명령을 찾을 수 없는 경우 실패한 종료 상태를 반환하지 않는다는 점에서 type스크립트와 비슷한 문제를 겪었습니다. 또한 실행 파일의 경우 그 대신에 이와 유사한 것을 출력하는 which것과 달리 스크립트에서 사용하기가 덜 쉽습니다.whichls is /bin/ls/bin/ls

Unix 버전 8(대중에게 공개되지 않음)의 Bourne 쉘에 내장된 함수의 type이름이 바뀌고 whatis인수 및 인쇄 함수 정의도 보고하도록 확장되었습니다. 또한 type이름을 찾을 수 없을 때 실패가 반환되지 않는 문제를 해결합니다 .

rc, Plan9(일회성 Unix 후속) 쉘(및 akanga및 같은 파생 제품 es) whatis도 사용할 수 있습니다.

sh80년대 중반에 개발되었지만 1988년 이전에는 널리 사용되지 않은 Korn 셸( POSIX 정의의 기반이 되는 하위 집합) csh은 Bourne 셸 위에 많은 기능(라인 편집기, 별칭...)을 추가했습니다. 여기에는 몇 가지 옵션( 유사하게 자세한 출력을 제공하고 실행 파일만 검색(별명/함수 대신...))을 사용하는 자체 whence내장 함수( )가 추가됩니다.type-vtype-p

AT&T와 Berkeley 간의 저작권 분쟁과 동시에 일부무료 소프트웨어쉘 구현은 1980년대 후반과 1990년대 초반에 나타났습니다. Almquist 쉘(BSD의 Bourne 쉘을 대체함) ash(FSF 후원)의 모든 공개 도메인 구현은 1989년에서 1991년 사이에 나왔습니다.kshpdkshbashzsh

Ash는 Bourne 쉘을 대체하도록 의도되었지만 type훨씬 나중에(NetBSD 1.3 및 FreeBSD 2.3에서)까지 내장 기능이 없었습니다 hash -v. 그러나 OSF/1에는 OSF까지 항상 0을 반환하는 내장 기능이 /bin/sh있었습니다 . type/1 v3.x. bash아무것도 추가되지 않았지만 경로( like ) 및 보고서를 인쇄 하는 옵션이 whence추가되었습니다.-ptypetype -pwhence -p-a모두일치하는 명령. s -like 명령이 tcsh내장되어 which추가되었습니다 . 모두.wherebashtype -azsh

Shell fish(2005)에는 type명령이 함수로 구현되어 있습니다.

동시에 csh 스크립트는 whichNetBSD에서 제거되었으며(tcsh에 내장되어 있었고 다른 쉘에서는 많이 사용되지 않았기 때문에) 기능이 추가되었습니다 ( 로 호출 whereis될 때 ). OpenBSD와 FreeBSD에서는 이 역시 C로 작성되고 .whichwhereiswhich$PATHwhich$PATH

구현하다

다양한 Unices에는 명령에 대한 수십 가지의 which다양한 구문과 동작 구현이 있습니다.

tcshLinux에서는 ( 및 의 내장 구현 외에도 zsh) 여러 구현을 찾을 수 있습니다. 예를 들어, 최근 Debian 시스템에서는 $PATH.

busybox명령이 하나 더 있습니다 which.

하나는 GNU which아마도 가장 고급스러운 것일 것입니다. csh 스크립트의 기능을 which다른 쉘로 확장하려고 시도합니다. 별칭과 기능이 무엇인지 알려 주어 더 나은 답변을 제공할 수 있습니다(일부 Linux 배포판에는 이에 대해 전역 별칭이 설정되어 있다고 생각합니다 bash).

zsh몇 개 있어요운영자실행 파일 경로로 확장됩니다.= 파일 이름 확장자연산자 및 :c기록 확장 수정자(여기서는 다음에 적용됨)매개변수 확장):

$ print -r -- =ls
/bin/ls
$ cmd=ls; print -r -- $cmd:c
/bin/ls

zsh, zsh/parameters명령 해시 테이블은 commands모듈에서 연관 배열로도 사용됩니다.

$ print -r -- $commands[ls]
/bin/ls

이 유틸리티( Unix V8 Bourne 셸 또는 Plan 9/에 있는 유틸리티 whatis제외 )는 문서(greps Whatis 데이터베이스, 즉 매뉴얼 페이지 개요)에만 사용되므로 실제로 관련이 없습니다.rces

whereis3BSD실행 파일, 매뉴얼 페이지, 소스 코드를 동시에 찾는 데 사용되는 대신 현재 환경을 기반으로 하지 않고 마치 which에 작성된 것처럼 동시에 추가됩니다 . 다시 말하지만, 이는 다른 요구 사항을 충족합니다.Ccsh

이제 표준 측면에서 POSIX는 command -vand -V명령(POSIX.2008까지 선택 사항임)을 지정합니다. UNIX 지정 type명령(옵션 없음) 그게 전부입니다 ( where, which, whence는 어떤 표준에서도 지정되지 않습니다).

일부 버전까지는 typecommand -vLinux Standard Base 사양에서 선택 사항이었습니다. 이는 예를 들어 일부 이전 버전 posh(두 가지 모두를 기반으로 함에도 불구하고 pdksh)이 둘 중 하나도 갖지 않은 이유를 설명합니다. command -v일부 Bourne 쉘 구현에도 추가되었습니다(예: Solaris).

오늘의 상태

현재 상황은 typeBourne과 같은 모든 쉘에 공통적입니다. (@jarno가 지적했듯이 아래 설명의 "POSIX 모드가 아닐 때" 또는 Almquist의 일부 후손의 command -v경고/오류에 유의하십시오. 껍데기 ). 사용하려는 유일한 쉘입니다(거기에 쉘이 없고 내장되어 있기 때문입니다).bashtcshwhichtypewhich

tcsh쉘 외부의 쉘에서 zshwhich시작 파일 중 하나 또는 일부에 동일한 이름을 가진 별칭이나 함수가 없고 이에 대해 정의된 별칭이나 함수가 없는 한 ~/.cshrc, 이는 ~/.bashrc사용자 에게 알려줄 수도 있고 그렇지 않을 수도 있습니다. 잘못된 것을 말해요.$PATH~/.cshrc

특정 이름의 모든 명령을 알고 싶다면 이식 가능한 것은 없습니다. wherein tcsh또는 in zsh또는 in ksh93 및 기타 쉘을 사용할 수 있으며 이를 결합할 수 있습니다.type -abashzshwhence -atypewhich -a

제안

실행 파일의 경로 이름을 가져옵니다.

이제 스크립트에서 실행 파일의 경로 이름을 얻으려면 몇 가지 고려 사항이 있습니다.

ls=$(command -v ls)

표준 접근 방식이 될 것입니다.

그러나 몇 가지 문제가 있습니다.

  • 실행 파일을 실행하지 않으면 해당 경로를 아는 것이 불가능합니다. 모두 type, which, command -v... 경험적 방법을 사용하여 경로를 찾습니다. 구성 요소를 반복하여 $PATH실행 권한이 있는 디렉터리가 아닌 첫 번째 파일을 찾습니다. 그러나 셸에 따라 많은 명령(Bourne, AT&T ksh, zsh, ash...)은 시스템 호출이 오류를 반환하지 않을 $PATH때까지 순차적으로만 실행됩니다. execve예를 들어, $PATH포함되어 /foo:/bar있고 실행하려는 경우 ls먼저 실행을 시도하고, /foo/ls그렇지 않으면 실패합니다 /bar/ls. 이제 /foo/ls실행 권한이 없기 때문에 실행이 실패할 수도 있지만 유효한 실행 파일이 아닌 등의 여러 가지 이유로 인해 실행이 실패할 수도 있습니다. 실행 권한이 있는지 command -v ls보고 하지만 /foo/ls유효한 실행 파일이 아닌 경우 /foo/ls실행이 ls실제로 실행될 수 있습니다 ./bar/ls/foo/ls
  • foo내장 함수, 함수 또는 별칭인지 여부를 반환 command -v foo합니다 foo. 또는 ash와 같은 일부 셸의 pdksh경우 빈 문자열이 포함되고 현재 디렉터리에 실행 파일이 있으면 zsh반환될 수도 있습니다 . 어떤 경우에는 이를 고려해야 할 수도 있습니다. 내장 함수 목록은 셸 구현(때때로 busybox의 내장 함수)에 따라 다르며, 예를 들어 환경에서 함수를 얻을 수 있다는 점을 기억하세요.foo$PATHfoomountshbash
  • $PATH상대 경로 구성 요소가 포함된 경우 (일반적 .으로 또는 둘 다 현재 디렉터리를 참조하지만 무엇이든 될 수 있는 빈 문자열) 쉘에 따라 command -v cmd절대 경로가 출력되지 않을 수 있습니다. 따라서 달리는 동안 얻은 경로는 command -v다른 곳에 도달한 후에는 더 이상 유효하지 않습니다.cd
  • 일화: ksh93 셸을 사용하는 경우 /opt/ast/bin(정확한 경로는 시스템에 따라 다를 수 있다고 생각하지만) $PATHksh93은 추가 내장 기능( chmod, cmp, cat...)을 제공하지만 해당 경로가 존재하지 않는 경우 command -v chmod에도 또한 /opt/ast/bin/chmod존재하지 않는 것을 반환합니다.

명령이 존재하는지 확인

특정 명령이 표준으로 존재하는지 확인하려면 다음을 수행할 수 있습니다.

if command -v given-command > /dev/null 2>&1; then
  echo given-command is available
else
  echo given-command is not available
fi

사람들이 사용하고 싶어할 만한 곳which

(t)csh

csh및 에서는 tcsh선택의 여지가 많지 않습니다. 에서는 이것이 내장되어 있기 tcsh때문에 좋습니다 . which이는 csh시스템 which명령이며 경우에 따라 원하는 작업을 수행하지 못할 수도 있습니다.

특정 쉘에서만 명령 찾기

이를 사용하는 것이 합리적일 수 있는 상황은 쉘 스크립트에서 잠재적인 쉘 내장 기능이나 함수를 무시 which하고 명령의 경로를 알고 싶은 경우입니다 bash. csh즉 , ( 예 : 또는 ) , (예 : , ), ( , ) 또는 시스템에서 사용할 수 있고 스크립트가 아닌 내장 함수 (예: 또는 )입니다.tcshdashBournewhence -pkshzshcommand -evyashwhatis -prcakangawhichtcshzshwhichcsh

이러한 조건이 충족되면 다음이 수행됩니다.

echo=$(which echo)

echo쉘 내장/별칭/함수인지 여부에 $PATH관계없이 (극단적인 경우를 제외하고) 첫 번째 항목에 대한 경로를 제공합니다 .echo

다른 쉘에서는 다음을 선호합니다.

  • 다루기 힘든: echo==echo또는 echo=$commands[echo]또는echo=${${:-echo}:c}
  • 변화 많은,다루기 힘든:echo=$(whence -p echo)
  • 야쉬:echo=$(command -ev echo)
  • RC,아캉가:( echo=`whatis -p echo`공백이 있는 경로를 참고하세요)
  • 물고기:set echo (type -fp echo)

당신이하고 싶은 모든 것이 있다면달리기echo명령을 사용하면 경로를 가져올 필요 없이 다음과 같이 할 수 있습니다.

env echo this is not echoed by the builtin echo

tcsh예를 들어 내장 함수 의 사용을 방지하려면 를 사용하십시오 .which

set Echo = "`env which echo`"

정말 외부 명령이 필요할 때

사용하고 싶을 수 있는 또 다른 상황 which은 실제로필요외부 명령. POSIX에서는 모든 셸 내장 명령(예: )을 외부 명령으로도 사용할 수 있어야 하지만 불행하게도 많은 시스템에서는 command그렇지 않습니다. 예를 들어, 이 명령은 Linux 기반 운영 체제에서는 거의 발견되지 않지만 command대부분의 운영 체제에서는 사용할 수 있습니다(운영 체제마다 옵션과 동작이 다르지만).commandwhich

외부 명령이 필요할 수 있는 상황은 POSIX 셸을 호출하지 않고 명령을 실행하려는 경우입니다.

system("some command line")C 또는 다양한 언어, ... 함수는 popen()셸을 호출하여 명령줄을 구문 분석하므로 system("command -v my-cmd")그 안에서 작업합니다. 한 가지 예외는 perl쉘에 쉘 특수 문자(공백 제외)가 표시되지 않으면 쉘이 최적화된다는 것입니다. 이는 백틱 연산자와도 작동합니다.

$ perl -le 'print system "command -v emacs"'
-1
$ perl -le 'print system ":;command -v emacs"'
/usr/bin/emacs
0

$ perl -e 'print `command -v emacs`'
$ perl -e 'print `:;command -v emacs`'
/usr/bin/emacs

:;위의 내용을 추가하면 perl쉘이 강제로 호출됩니다. 를 사용하면 which이 트릭을 사용할 필요가 없습니다.

답변2

사람들이 그것을 사용하고 싶어하지 않는 이유는 which이미 설명되었지만 실제로 실패한 시스템의 몇 가지 예는 다음과 같습니다 which.

Bourne과 같은 쉘에서는 which출력과 출력을 비교합니다 type( type쉘 내장으로서 명령을 호출하는 방법을 알려주는 쉘이기 때문에 이것이 사실이어야 합니다).

많은 경우에 그것은모서리하지만 명심하세요 which/ type종종 극단적인 경우에 사용됩니다(예기치 않은 동작에 대한 답을 찾기 위해, 예:이 명령은 대체 왜 이렇게 작동하며 어떤 명령을 부르고 있습니까?).

대부분의 시스템, 대부분의 Bourne 유사 쉘: 기능

가장 확실한 경우는 함수입니다.

$ type ls
ls is a function
ls ()
{
[ -t 1 ] && set -- -F "$@";
command ls "$@"
}
$ which ls
/bin/ls

그 이유는 which실행 파일과 때로는 별칭에 대한 정보만 보고되기 때문입니다(항상 그런 것은 아니지만).당신의쉘), 함수가 아닙니다.

GNU 매뉴얼 페이지에는 함수를 보고하는 데 사용하는 방법에 대한 깨진(인용을 잊어버렸기 때문에 $@) 예제가 있지만 별칭과 마찬가지로 쉘 구문 분석기를 구현하지 않기 때문에 속이기 쉽습니다.

$ which() { (alias; declare -f) | /usr/bin/which --tty-only --read-alias --read-functions --show-tilde --show-dot "$@";}
$ f() { echo $'\n}\ng ()\n{ echo bar;\n}\n' >> ~/foo; }
$ type f
f is a function
f ()
{
echo '
}
g ()
{ echo bar;
}
' >> ~/foo
}
$ type g
bash: type: g: not found
$ which f
f ()
{
echo '
}
$ which g
g ()
{ echo bar;
}

대부분의 시스템, 대부분의 Bourne 유사 쉘: 내장

또 다른 명백한 사례는 내장 명령이나 키워드입니다. which외부 명령으로는 쉘에 내장된 것이 무엇인지 알 수 있는 방법이 없기 때문입니다(일부 쉘은 zsh내장 내장을 동적으로 로드할 수 있음) bash.ksh

$ type echo . time
echo is a shell builtin
. is a shell builtin
time is a shell keyword
$ which echo . time
/bin/echo
which: no . in (/bin:/usr/bin)
/usr/bin/time

zsh(이것은 내장된 곳에는 적용되지 않습니다 )which

Solaris 10, AIX 7.1, HP/UX 11i, Tru64 5.1 등:

$ csh
% which ls
ls:   aliased to ls -F
% unalias ls
% which ls
ls:   aliased to ls -F
% ksh
$ which ls
ls:   aliased to ls -F
$ type ls
ls is a tracked alias for /usr/bin/ls

이는 대부분의 상용 Unices which(3BSD의 원래 구현과 같은) 에서 csh읽기 이기 때문입니다 ~/.cshrc. 현재 정의하고 있는 별칭과 실제로 사용하는 쉘에 관계없이 보고할 별칭은 거기에 정의된 것입니다. 그렇죠.

HP/UX 또는 Tru64의 경우:

% echo 'setenv PATH /bin:/usr/bin' >> ~/.cshrc
% setenv PATH ~/bin:/bin:/usr/bin
% ln -s /bin/ls ~/bin/
% which ls
/bin/ls

$path(Solaris 및 AIX 버전에서는 읽기 전에 저장 ~/.cshrc하고 명령을 찾기 전에 복원하여 이 문제를 해결했습니다 .)

$ type 'a b'
a b is /home/stephane/bin/a b
$ which 'a b'
no a in /usr/sbin /usr/bin
no b in /usr/sbin /usr/bin

또는:

$ d="$HOME/my bin"
$ mkdir "$d"; PATH=$PATH:$d
$ ln -s /bin/ls "$d/myls"
$ type myls
myls is /home/stephane/my bin/myls
$ which myls
no myls in /usr/sbin /usr/bin /home/stephane/my bin

(물론, csh스크립트로서 공백이 포함된 인수를 처리할 것이라고 기대할 수는 없습니다...)

CentOS 6.4, 배시

$ type which
which is aliased to `alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'
$ alias foo=': "|test|"'
$ which foo
alias foo=': "|test|"'
        /usr/bin/test
$ alias $'foo=\nalias bar='
$ unalias bar
-bash: unalias: bar: not found
$ which bar
alias bar='

해당 시스템에서는 GNU 명령을 래핑하기 위해 시스템 전체에 별칭이 정의됩니다 which.

가짜 출력은 s의 출력을 읽었 which지만 올바르게 구문 분석하는 방법을 모르고 휴리스틱(한 줄에 하나의 별칭, , , ... 다음에 처음 발견된 명령 찾기)을 사용하기 때문입니다.bashalias|;&

CentOS의 가장 나쁜 점은 zsh완벽하게 좋은 which내장 명령이 있지만 CentOS는 이를 작동하지 않는 GNU 별칭으로 대체하여 이를 중단시킨다는 것입니다 which.

데비안 7.0, ksh93:

(여러 개의 쉘이 있는 대부분의 시스템에 적용 가능하지만)

$ unset PATH
$ which which
/usr/local/bin/which
$ type which
which is a tracked alias for /bin/which

데비안에서는 스크립트 /bin/which입니다 /bin/sh. 내가 아는 한 sh그것은 그렇지만 , dash그럴 때도 마찬가지다 bash.

설정을 해제해도 조회가 PATH비활성화되지는 않습니다 PATH. 이는 시스템의기본 경로불행하게도 데비안에서는 아무도 동의하지 않습니다( dash그리고 bashhas /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin, zshhas /bin:/usr/bin:/usr/ucb:/usr/local/bin, ksh93has /bin:/usr/bin, mkshhas /usr/bin:/bin( $(getconf PATH)) , execvp()(like env) has :/bin:/usr/bin(예, 먼저 현재 디렉터리를 살펴보세요!)).

그렇기 때문에 위의 경우와 다른 기본값을 which사용하기 때문에 오류가 발생합니다 .dashPATHksh93

GNU는 더 나은 서비스를 제공하지 않으며 which다음과 같이 보고합니다.

which: no which in ((null))

/usr/local/bin/whichakanga(흥미롭게도 내 시스템에는 실제로 스크립트와 함께 제공되는 스크립트가 있습니다 akanga( rc기본값은 셸 파생입니다 PATH) /usr/ucb:/usr/bin:/bin:.)

bash, 모든 시스템:

유일한 사람Chris는 그의 답변에서 언급했습니다.:

$ PATH=$HOME/bin:/bin
$ ls /dev/null
/dev/null
$ cp /bin/ls bin
$ type ls
ls is hashed (/bin/ls)
$ command -v ls
/bin/ls
$ which ls
/home/chazelas/bin/ls

또한 hash수동으로 호출한 후:

$ type -a which
which is /usr/local/bin/which
which is /usr/bin/which
which is /bin/which
$ hash -p /bin/which which
$ which which
/usr/local/bin/which
$ type which
which is hashed (/bin/which)

which이제 때때로 실패하는 부분은 다음과 같습니다 type.

$ mkdir a b
$ echo '#!/bin/echo' > a/foo
$ echo '#!/' > b/foo
$ chmod +x a/foo b/foo
$ PATH=b:a:$PATH
$ which foo
b/foo
$ type foo
foo is b/foo

이제 일부 쉘을 사용하십시오.

$ foo
bash: ./b/foo: /: bad interpreter: Permission denied

다른 사람:

$ foo
a/foo

실행될 수 없다는 사실을 사전에 알 수도 없고 whichtype수도 없습니다 . b/foo일부 쉘(예: bash, ksh또는 )은 yash호출될 때 foo실제로 실행을 시도 b/foo하고 오류를 보고하는 반면, 다른 쉘(예 zsh: , ash, , csh, Bourne, )은 시스템 호출이 실패할 경우 실행됩니다 tcsh.a/fooexecve()b/foo

답변3

Stephane이 언급하지 않은 것 중 하나는 (내 빠른 보기에서) which쉘의 경로 해시 테이블을 모른다는 것입니다. 그 결과 실제 실행 결과를 나타내지 않는 결과가 반환될 수 있어 디버깅에 쓸모가 없게 됩니다.

답변4

(이 질문에는 "이식성"이라는 태그가 붙어 있으므로 "대화형 사용" 및 "사용 중인 시스템에만 관심이 있습니다"를 포함하여 질문에 대한 다양한 해석이 제외됩니다. 따라서 이 답변에서는 다음 사항만 고려합니다.쉘 스크립트에서 이 작업을 수행하면 안되는 이유는 무엇입니까?)

Stéphane은 옵션에 대한 철저한 분석을 제공하고 각 옵션이 좋은지 나쁜지에 대한 이유를 제시합니다. 어떤 이유도 which항상 틀린 답은 아닙니다. 대신에 작은 영향을 미치는 요소가 많이 있습니다. 그 중 하나 또는 몇 가지를 견딜 수는 있지만 함께 복용하면 마음이 바뀔 수 있습니다.

아직 언급되지 않은 것을 사용하지 않는 한 가지 이유는 which다음과 같은 질문을 낳기 때문입니다.

which애초에 왜 출력을 원하는가?

내가 의미하는 바는 때때로 목표는 드롭인 대체품을 찾는 것이 아니라 which애초에 필요하지 않은 방식으로 코드를 리팩터링하는 것입니다.

일반적인 패턴은 다음과 같습니다.

CMD=$( which cmd )

# some time later...
$CMD --some --args

거의 항상 인용되지 않는다는 사실을 제쳐두고 $CMD, 전체 패턴이 깨졌다고 생각합니다.

CMD=/my/path/to/cmd종속성을 피하는 것이 목적인 경우 하드 코딩이 중요합니다 $PATH.

그러나 어쨌든 검색하면 $PATH거의 항상 의미가 없습니다. which및를 완전히 제거 $CMD하고 다음을 작성하십시오.

cmd --some --args

함수나 별칭에 경로를 포함시키려고 경로가 필요하다고 생각되면 command해당 경로를 사용하세요.

function cmd {
    command cmd --extra-arg "$@"
}

물론 예외도 있으며, 다른 것을 기반으로 해야 합니다 PATH. 이 경우 다음을 사용하는 것이 좋습니다.

function cmd {
    PATH=$OTHERPATH command cmd "$@"
}

전체적으로 요점은피하다"명령 경로를 얻는 방법"에 대한 정신적 지름길이 있지만 사례별로 생각해 보세요.이 길이 정말 필요한가요? 왜?

어떤 경우에는 which허용될 수도 있지만 적어도 어느 정도는 더 나은 해결책이 있을 것입니다. 다른 사람들을 위한 튜토리얼을 작성하고 있다면 다른 옵션에 대해 더 자세히 생각해 보세요.


합리적 이라고 생각하더라도 CMD=$( which cmd ) ; ... $CMD args피해야 할 다른 이유가 있습니다.which

이것은 보편적이지 않습니다

이것POSIX에 필요한 명령 목록을 포함 command하지만 type포함하지 않습니다 which.

출력 형식이 지정되지 않았습니다.

which의무만 있으면 돼보여주다설명 없이 그대로 명령을 제공하는 대신 명령 경로를 지정하십시오. 대부분의 버전이지만.

which foo다음을 출력하는 것은 완벽하게 합법적입니다:

  • foo might be /opt/foobar/bin/foo (unverified);또는
  • foo is in /bin;또는
  • ~foo/bin/foo( ~foofoo의 홈 디렉토리를 나타냄) 또는
  • /proc/1234/root/bin/foo(프로세스 1234 종료)

답변이 최신이 아닐 수 있습니다.

(이것은 기본적으로 모든 형식을 제외하며 CMD=$( something ) ; ... $CMD ...에 국한되지 않습니다 which.)

특정 명령 이름에 대한 실행 파일의 위치는 다양한 이유로 변경될 수 있습니다.

때로는 이전 위치를 원할 때도 있고 그렇지 않을 때도 있습니다.

명령에 대한 경로가 상대적인 경우도 있습니다(따라서 .시작이 아님 /). 이는 일반적으로 보안 위험으로 간주되므로 드물지만 단순히 실행하면 cd프로그램 경로가 무효화될 수 있는 특별한 경우에는 여전히 적용될 수 있습니다.

관련 정보