왜 "뭐"를 사용하지 않습니까? 그렇다면 무엇을 사용해야합니까?


328

실행 파일의 경로를 찾고 또는 유닉스 쉘에서 명령 이름을 입력하면 무슨 일이 일어날 지 확인해 볼 때, 다른 유틸리티의 과다 (있다 which, type, command, whence, where, whereis, whatis, hash, 등).

우리는 종종 그 사실 which을 피해야한다고 들었습니다 . 왜? 대신 무엇을 사용해야합니까?


3
나는 사용 which에 대한 대부분의 주장 은 대화 형 쉘 컨텍스트를 가정한다고 생각 합니다. 이 질문은 / portability로 태그되었습니다. 그래서 나는이 문맥에서 질문을 "" which에서 주어진 이름의 첫 번째 실행 파일을 찾는 대신에 무엇을 사용해야하는지 "로 해석합니다 $PATH. 대부분의 which실제 포터블 쉘 스크립트에서 학문적 관심사 인 별칭, 내장 및 기능을 다루는 대부분의 답변과 이유 . 로컬로 정의 된 별칭은 쉘 스크립트를 실행할 때 상속되지 않습니다 (로 소스를 지정하지 않는 한 .).
MattBianco

5
@MattBianco, 예. csh( 대부분의 상용 Unices에서 which여전히 csh스크립트 임) ~/.cshrc비 대화식 일 때 읽습니다 . 그래서 csh 스크립트가 일반적으로로 시작하는 것을 알 수 #! /bin/csh -f있습니다. 별칭을 제공 which하는 것을 목표 로 하지 않기 때문에 (대화 형) 사용자를위한 도구로 사용되기 때문입니다 csh. POSIX 쉘 사용자는 command -v.
Stéphane Chazelas

@rudimeier, 그 대답은 될 쉘이 아닌 경우 항상 (t)csh(또는 당신은 당신에게 정확한 결과를 제공하지 않는 경우 괜찮다)를 사용 type하거나 command -v대신 . 이유에 대한 답변을 참조하십시오 .
Stéphane Chazelas

1
@rudimeier, ( 의 사용법뿐만 아니라 stat $(which ls)여러 가지 이유로 ( --따옴표 누락 , 따옴표 누락) 잘못되었습니다 which). 을 사용 stat -- "$(command -v ls)"합니다. 그것은 ls실제로 파일 시스템에서 찾은 명령 이라고 가정 합니다 (쉘의 내장 기능이나 별칭 기능이 아님). which잘못된 경로 (입력 한 경우 쉘이 실행하는 경로가 아님 ls)를 제공하거나 다른 쉘 구성에 정의 된 별명을 제공 할 수 있습니다.
Stéphane Chazelas

1
@rudimeier, 다시 말하지만, 많은 which구현이 ( 쉘에서 호출 할 수 있는 것에 관계없이) ls조회로 찾을 수있는 조건 조차 제공하지 않는 여러 조건 이 있습니다 . 또는 정답을 제공 할 가능성이 높습니다. 여기서 요점 은의 유산이 깨 졌다는 것 입니다. $PATHlssh -c 'command -v ls'zsh -c 'rpm -q --whatprovides =ls'whichcsh
Stéphane Chazelas

답변:


366

여기에 대해 알고 싶지 않다고 생각한 모든 것이 있습니다.

요약

Bourne과 같은 쉘 스크립트에서 실행 파일의 경로 이름을 얻으려면 몇 가지주의 사항이 있습니다 (아래 참조).

ls=$(command -v ls)

주어진 명령이 존재하는지 확인하려면 :

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

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

type ls

which명령은 C-Shell의 유산으로, Bourne과 같은 껍질에 남겨 두는 것이 좋습니다.

사용 사례

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

쉘 프롬프트에서 일반적인 유스 케이스는 다음과 같습니다 . 이 명령은 이상하게 동작합니다. 올바른 것을 사용하고 있습니까? 입력 할 때 정확히 어떤 일이 발생 mycmd했습니까? 그것이 무엇인지 더 볼 수 있습니까?

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

쉘 스크립트에서는 상당히 다른 경향이 있습니다. 쉘 스크립트에는 명령이 어디에서 무엇을 원하는지 알고 싶은 이유가 없습니다. 일반적으로 알고 싶은 것은 실행 파일의 경로이므로 다른 파일에 대한 경로와 같은 추가 정보를 얻거나 해당 경로의 실행 파일 내용에서 정보를 읽을 수 있습니다.

대화식으로 시스템 에서 사용 가능한 모든my-cmd 명령을 스크립트로 거의 알지 못할 수도 있습니다 .

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

역사

먼저 역사가 조금 있습니다.

70 년대 후반까지 초기 유닉스 쉘에는 기능이나 별명이 없었습니다. 의 기존 실행 파일 만 검색합니다 $PATH. csh1978 주위 도입 별칭 (비록 csh처음 발표2BSD도의 처리 년 5 월 1979 년), 그리고 .cshrc(로, 모든 쉘 쉘을 사용자 정의하는 사용자를 위해 csh읽어 .cshrc도하지 않을 경우 대화 형 스크립트처럼).

Bourne 쉘은 1979 년 초 Unix V7에서 처음 릴리스되었지만 기능 지원은 훨씬 나중에 추가되었지만 (1984 년 SVR2에서), 어쨌든 rc파일 이 없었습니다 ( .profile자체가 아닌 환경을 구성하는 것입니다 ).

csh Bourne 쉘보다 훨씬 인기가 많았습니다 (Bourne 쉘보다 구문이 심하게 나빴지 만) 대화 형 사용을 위해 더 편리하고 멋진 기능을 많이 추가했습니다.

3BSD(1980)하는 whichCSH 스크립트 추가 된 csh실행 파일을 식별하는 데 도움이 사용자, 그리고 당신이로 찾을 수있는 거의 다른 스크립트입니다 which(솔라리스, HP / UX, AIX 또는 Tru64를 같은) 요즘 많은 상용 유닉스에.

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

여기 which에서 가장 인기있는 쉘을 csh찾았습니다 (90 년대 중반까지 인기가있었습니다). 이것은 책에 기록되어 여전히 널리 사용되는 주된 이유입니다.

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

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

type내장 명령으로 SVR2에서 1984 년까지 유사한 기능이 Bourne 쉘에 추가되지 않았습니다 . 그것이 외부 스크립트와 달리 내장되어 있다는 사실 은 쉘의 내부에 액세스 할 수 있으므로 올바른 정보를 어느 정도 제공 할 있음을 의미합니다 .

초기 type명령은 which명령을 찾을 수없는 경우 실패 종료 상태를 리턴하지 않는다는 점 에서 스크립트 와 유사한 문제로 인해 어려움을 겪었습니다 . 또한 실행 파일의 경우와 달리 스크립트에서 사용하기 쉽지 않은 대신 which비슷한 것을 출력합니다 .ls is /bin/ls/bin/ls

유닉스 버전 8 (와일드 버전으로 출시되지 않음) Bourne 쉘의 type기본 이름은으로 변경되었습니다 whatis. 그리고 Plan9 (유닉스의 번 - 될 후계자) 쉘 rc(와 같은 그 유도체 akangaes)가 whatis아니라.

80 년대 중반에 개발되었지만 1988 년 이전에 널리 사용되지 않았던 Korn 쉘 (POSIX sh 정의의 기반이되는 서브 세트) csh은 Bourne 쉘 위에 많은 기능 (라인 편집기, 별명 ...)을 추가했습니다. . 자체 옵션 whence을 추가하여 type( -vtype같은) 여러 옵션을 취했습니다 ( 유사한 자세한 출력 을 제공하고 -p별칭 / 함수가 아닌 실행 파일 만 찾습니다).

AT & T와 버클리 사이의 저작권 문제와 관련하여 혼란과 동시에, 90 년대 초반에 몇 가지 무료 소프트웨어 셸 구현이 나왔습니다. ksh (pdksh)의 퍼블릭 도메인 구현 인 FS에서 bash후원 하는 Almquist 쉘 (ash, BSD에서 Bourne 쉘을 대체하기 위해 재) zsh은 1989 년에서 1991 년 사이에 나왔습니다.

Ash는 Bourne 쉘을 대체하기위한 것이었지만 typeNetBSD 1.3 및 FreeBSD 2.3에서 훨씬 나중에까지 내장 기능이 없었습니다 hash -v. OSF / 1 /bin/sh에는 typeOSF / 1 v3.x까지 항상 0을 리턴 하는 내장 기능이있었습니다. bash추가하지 않은 whence만이 추가 -p하는 옵션 type의 경로를 인쇄하려면을 ( type -p같은 것 whence -p)과 -a보고 모든 일치하는 명령을. tcsh만든 which내장하고 추가 where처럼 행동 명령 bash'들 type -a. zsh그들 모두를 가지고 있습니다.

fish쉘 (2005)는이 type함수로 구현 명령을 사용합니다.

which(이 tcsh가 등 껍질에 많이 사용하지 않을의 내장되면서) 사이에 NetBSD의에서 제거 CSH 스크립트 및 추가 기능 whereis(로 호출하는 경우는 which, whereis같은 행동 which단지에서 실행을 보이는 것을 제외하고 $PATH). OpenBSD와 FreeBSD에서는 which명령 $PATH만 찾는 C로 작성된 것으로 변경되었습니다 .

구현

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

Linux에서 ( tcsh와 내장 된 것 외에 zsh) 몇 가지 구현이 있습니다. 예를 들어 최근 데비안 시스템에서는에서 명령을 찾는 간단한 POSIX 셸 스크립트입니다 $PATH.

busybox또한이 which명령을 사용합니다.

GNU which아마도 가장 사치스러운 일이다. whichcsh 스크립트가 수행 한 작업을 다른 쉘로 확장하려고 시도 합니다. 별명과 함수가 무엇인지 알려 주면 더 나은 답변을 얻을 수 있습니다 (일부 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

whatis유틸리티 (Unix V8 Bourne 쉘 또는 Plan 9 rc/ 의 유틸리티 제외 es)는 문서 용이므로 실제로는 관련이 없습니다 (whatis 데이터베이스, 즉 매뉴얼 페이지 개요).

whereis또한 추가되었습니다 3BSD과 동시에 which이 작성되었습니다하지만 C,하지 csh동시에, 현재의 환경에 따라 실행, 매뉴얼 페이지와 소스가 아니라에서 조회하는 데 사용됩니다. 다시 말하지만, 그것은 다른 요구에 답합니다.

이제 표준 정면에서 POSIX는 command -v-V명령을 지정했습니다 (POSIX.2008까지는 선택 사항이었습니다). UNIX는 type명령을 지정합니다 (옵션 없음). 그건 모든 ( where, which, whence표준에 지정되지 않은)

일부 버전까지, type그리고 command -vLinux Standard Base 사양에서 선택 사항으로, 예를 들어 이전 버전 posh( pdksh둘 다를 기반으로 함 )이없는 이유를 설명 합니다. command -v또한 일부 Bourne 쉘 구현에 추가되었습니다 (Solaris 에서처럼).

오늘 상태

상태는 요즘 그입니다 typecommand -v모든 본쉘 (@jarno에서 언급 한 바와 같이하지만,의주의 / 버그주의에 편재 bash하지 POSIX 모드 또는 Almquist의 일부 자손의 의견은 아래 쉘 때를). tcsh사용하려는 것이 유일한 셸 which(가 전혀 없습니다로 type거기 which내장되어있다).

이외의 껍질에서 tcsh하고 zsh, which한이 별명 또는 함수 같은 이름으로 우리의에서 없다 당신에게 주어진 실행 파일의 경로를 말할 수있다 ~/.cshrc, ~/.bashrc또는 쉘 시작 파일 및 사용자가 정의하지 않는 $PATH당신에 ~/.cshrc. 별명이나 함수가 정의되어 있으면 그것에 대해 말하거나 알려주지 않을 수 있습니다.

주어진 이름으로 모든 명령에 대해 알고 싶다면 이식성이 없습니다. 당신이 사용하는 거라고 where에서 tcsh또는 zsh, type -abash또는 zsh, whence -a경우 ksh93과 다른 쉘에서, 당신은 사용할 수 type와 함께 which -a하는 작동 할 수 있습니다.

추천

실행 파일의 경로 이름 가져 오기

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

ls=$(command -v ls)

표준 방법이 될 것입니다.

그래도 몇 가지 문제가 있습니다.

  • 실행 파일을 실행하지 않고는 경로를 알 수 없습니다. 모든 type, which, command -v... 모든 사용 휴리스틱 경로를 찾을 수 있습니다. $PATH구성 요소를 반복하고 실행 권한이있는 첫 번째 비 디렉토리 파일을 찾습니다. 그러나 셸에 따라 명령을 실행할 때 많은 명령 (Bourne, AT & T ksh, zsh, ash ...)은 시스템 호출이 오류와 함께 반환되지 않을 $PATH때까지 순서대로 실행합니다. execve. 예를 들어 $PATHcontains가 /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그것은 또한 반환 할 수 foo있는 경우 $PATH빈 문자열을 포함하고 실행 거기에 foo현재 디렉토리에있는 파일. 이를 고려해야 할 상황이 있습니다. 예를 들어 내장 목록은 셸 구현에 따라 다르며 (예를 들어 mountbusybox 용으로 내장 된 경우도 있음 sh), 예를 들어 bash환경에서 함수를 가져올 수 있습니다.
  • 경우 $PATH(일반적으로 상대 경로 구성 요소가 포함 .또는 두 가지 모두 현재 디렉토리를 참조하지만 아무것도 할 수있는 빈 문자열), 쉘에 따라 command -v cmd수도 출력되지 절대 경로. 따라서 달리는 시점에서 얻은 경로 command -vcd다른 곳에서 더 이상 유효하지 않습니다 .
  • 일화 : 경우 ksh93의 쉘과, /opt/ast/bin(즉, 정확한 경로는 내가 믿는 다른 시스템에서 다를 수 있지만)이 당신에 $PATH, ksh93의 사용 가능한 몇 가지 추가 내장 명령 (것 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

에서 cshtcsh, 당신은 선택의 여지가 없습니다. 에서 내장 된 tcsh것처럼 괜찮습니다 which. 에서 csh, 이것은 시스템 which명령이 될 것인데, 몇 가지 경우에는 원하는 것을 수행하지 못할 수도 있습니다.

일부 쉘에서만 명령 찾기

사용하는 which것이 합리적 인 경우는 명령 경로를 알고 싶거나 bash, csh( tcsh, dash) 또는 Bourne셸 스크립트 whence -p(예 : ksh또는 zsh) 가 아닌 셸 에서 잠재적 셸 내장 또는 기능을 무시하려는 경우 입니다 , command -ev(같은 yash), whatis -p( rc, akanga) 또는 내장 which(같은 tcsh또는 zsh시스템) which사용 가능하고 아닙니다 csh스크립트.

이러한 조건이 충족되면 다음을 수행하십시오.

echo=$(which echo)

당신에게 첫 번째의 경로 줄 것 echo에서을 $PATH(코너의 경우를 제외)에 관계없이 여부 echo도 쉘 내장 / 별칭 / 기능 여부로 발생합니다.

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

  • zsh을 : echo==echoecho=$commands[echo]또는echo=${${:-echo}:c}
  • ksh , zsh :echo=$(whence -p echo)
  • 야쉬 :echo=$(command -ev echo)
  • rc , akanga : echo=`whatis -p echo`(공백이있는 경로를 조심하십시오)
  • 물고기 :set echo (type -fp echo)

당신이 원하는 모든 경우 참고 실행 하는 echo명령을, 당신은 그것의 경로를 얻을 필요가 없습니다, 당신이 단지 수 :

env echo this is not echoed by the builtin echo

예를 들어 with tcsh를 사용하여 내장 which을 사용 하지 못하게하려면 다음을 수행하십시오.

set Echo = "`env which echo`"

외부 명령이 필요할 때

사용하려는 또 다른 경우 which는 실제로 외부 명령이 필요할 때 입니다. POSIX를 사용하려면 (와 같은 command) 모든 셸 내장 을 외부 명령으로 사용할 수 있어야하지만 불행히도 command많은 시스템 에서 그렇지 않습니다 . 예를 들어, commandLinux 기반 운영 체제 에서 명령 을 찾는 것은 드물지만 대부분은 which명령과 명령이 다릅니다 (다양한 옵션과 동작을 가진 명령이 있음).

외부 명령을 원할 경우 POSIX 쉘을 호출하지 않고 명령을 실행할 수 있습니다.

C 또는 다양한 언어 의 system("some command line"), 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하면 해당 트릭을 사용할 필요가 없습니다.


24
@Joe whichcsh많은 상용 Unices 의 스크립트입니다. 그 이유는 역사적이며, 내가 역사를 낸 이유입니다. 그래서 사람들은 그것이 어디에서 왔는지, 사람들이 그것을 사용하는 데 익숙한 이유와 실제로 그것을 사용해야 할 이유가없는 이유를 이해합니다. 그리고 그렇습니다. 어떤 사람들은 (t) csh를 사용합니다. 모두가 아직 리눅스를 사용하는 것은 아닙니다
Stéphane Chazelas

12
이 게시물을 읽은 후 답변에 대한 많은 맥락을 찾았지만 답변 자체는 아닙니다. 이 게시물의 어디에 실제로 사용 하지 말아야 하는지 which, 사용 which하려는 작업, 기록 which, 구현 which, 관련 명령을 수행하는 다른 명령 또는 실제로 사용하는 이유와 달리 사용하지 않는 이유는 which무엇입니까? 다른 명령이 더 나은 이유는 무엇 입니까? 그들은 어떻게 다른가 which? 함정을 어떻게 피할 수 있습니까? 이 답변은 실제로 문제보다 대안의 문제에 더 많은 단어를 사용합니다 which.
user62251

1
대답이 주장하는 것과 달리 command -v, 최소한 경로없이 순수한 파일 이름 인수로 호출하면 실행 권한을 확인하지 마십시오. 대시 0.5.8 및 GNU bash 4.3.48로 테스트했습니다.
jarno

2
@ StéphaneChazelas 내가 새 파일을 만든 touch /usr/bin/mytestfile다음 run을 실행 command -v mytestfile하면 경로 which mytestfile가 지정되지 않습니다.
jarno

2
@jarno, 네, 맞습니다. bash실행 파일을 찾을 수 없으면 실행 파일이 아닌 파일에 정착하므로 "확인"(실제로 는 오류 command -v/ type반환) mytestfile이지만 실행시 실행하려고하는 명령이므로 dash실행 파일 cmd보다 실행 파일 보다 앞에 실행 파일이없는 것처럼 command -v실행 cmd은 실행 파일 실행 파일을 실행하는 동안 실행 파일을 실행 하지 않는 것처럼 동작 합니다 (잘못된 파일도 해시 됨). FreeBSD sh(도 기반 ash)는 같은 버그를 가지고 있습니다. sh로 zsh, yash, ksh, mksh, bash는 정상입니다.
Stéphane Chazelas

47

사용하고 싶지 않은 이유 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

대부분의 상용 whichUnices (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

(Solaris 및 AIX 버전은 명령을 조회 $path하기 전에 파일을 읽고 ~/.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, bash

$ 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명령 을 래핑하는 시스템 전체에 정의 된 별칭이 있습니다.

가짜 출력이 있기 때문이다 which의 출력 읽어 bash들 ' alias만 제대로 구문 분석하는 방법을 알고하지 않고 추론 사용 (한 줄에 하나의 별칭을 먼저 한 후 명령을 발견 찾습니다 |, ;, &...)

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/whichA는 /bin/sh스크립트. 내 경우에는, sh존재 dash하지만이 때 그것은 동일합니다 bash.

미 설정은 PATH해제하지 않는 것입니다 PATH조회를하지만, 시스템의 사용을 의미합니다 기본 PATH 불행하게도 데비안에, 아무도에 동의하지 ( dashbash/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin, zsh/bin:/usr/bin:/usr/ucb:/usr/local/bin, ksh93/bin:/usr/bin, mksh/usr/bin:/bin( $(getconf PATH),) execvp()처럼 ( env)이 :/bin:/usr/bin예, 먼저 현재 디렉토리에 보이는 (! )).

이유는 which잘못이 사용하고 있기 때문에 위를 얻는다 dash'의 기본 PATH다른 ksh93S'를

다음과 which같이보고 하는 GNU 가 더 좋지 않습니다 .

which: no which in ((null))

(흥미롭게도 실제로 /usr/local/bin/which내 시스템에는 실제로 제공되는 akanga스크립트 인 akanga( rc기본값 PATH이 인 쉘 파생물 /usr/ucb:/usr/bin:/bin:.)이 있습니다.

bash, 모든 시스템 :

크리스는 그의 대답에 언급되어있다 :

$ 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/foo의 실패시 execve()에 시스템 호출 b/foo.


mksh실제로 기본값에 대해 다른 것을 사용합니다 $PATH. 먼저 운영 체제의 컴파일 타임 상수 _PATH_DEFPATH가 사용되며 (가장 일반적으로 BSD에서) confstr(_CS_PATH, …)사용되며 (POSIX) 사용되거나 존재하지 않거나 실패하는 경우 /bin:/usr/bin:/sbin:/usr/sbin사용됩니다.
mirabilos

1
첫 번째 예에서는 PATH에서 ls사용하는 함수 인 경우에도 마찬가지 ls입니다. 그리고 which사용되는 하나의 당신에게 괜찮 /usr/bin/ls/usr/local/bin/ls. 나는 보지 않는다 "왜 사용하지하는 ...."
rudimeier

@rudimeier, 즉 which ls저를 줄 것이다 /bin/ls상관없이 여부의 ls함수 호출하지 /bin/ls/opt/gnu/bin/ls또는 dir아무 것도 나. IOW, which(즉 어떤 구현, IMMV)는 관련이없는 뭔가주고있다
스테판 Chazelas가

1
@StephaneChazelas. 아니, 아니. 나는 이미 내 기능 이라는 것을 알고ls 있습니다. 내가 알고 내 것을 ls함수가 호출 ls에서 PATH. 이제 which파일의 위치를 ​​알려줍니다. "이 명령으로 쉘이 무엇을합니까?" 이 사용 사례 which가 잘못되었으므로 수정하십시오. 그러나 (GNU) which가 옳은 다른 사용 사례가 있습니다.
rudimeier

@rudimeter는 which구현에 따라 다릅니다 . 어떤 사람들은 그것이 별명이라고 말할 것입니다 (당신이 별명을 구성했거나 ~/.cshrc집에 그러한 별명을 가진 사람 이 있다면 ), 어떤 사람들은 어떤 조건 하에서 잘못된 경로를 줄 것입니다. sh -c 'command -v ls'완벽하지는 않지만 다른 요구 사항에 대한 올바른 답변을 제공 할 가능성이 높습니다 (그리고 표준이기도 함).
Stéphane Chazelas

21

Stephane이 언급하지 않은 것으로 보이는 것은 (내 빠른 탈지에서) which쉘의 경로 해시 테이블에 대해 전혀 모른다는 것입니다. 이는 실제로 실행되는 것을 나타내지 않는 결과를 반환하여 디버깅에 효과적이지 않은 결과를 초래할 수 있습니다.


6

유닉스 정신에서 : 각 프로그램이 가지 일을 잘 수행하게하십시오.

대답이 목표 인 경우 : 이름으로 어떤 실행 파일이 존재 합니까?

데비안 시스템과 함께 제공되는 실행 프로그램은 좋은 대답입니다. csh와 함께 제공되는 별명은 문제의 원인입니다. 내부 쉘로 제공되는 일부 쉘은 다른 목표를 가지고 있습니다. 해당 실행 파일을 사용하거나이 답변의 끝에 제공된 스크립트를 사용하십시오.

이 스크립트를 사용하면 깨끗하고 간단하며 유용합니다.

이 목표는 질문의 첫 문장과 일치합니다.

실행 파일 경로를 찾을 때……

실행 파일이없는 시스템 (대부분의 Linux 시스템에 있음)이없는 시스템이있는 경우 PATH 에서 ~/bin/which이전 /bin/에 시스템을 작성할 수 있으므로이 게시물의 맨 아래에있는 시스템과 같은 개인 실행 파일은 시스템 실행 파일보다 우선합니다.

해당 실행 파일은 기본적으로 PATH에있는 모든 실행 파일을 나열 합니다. 첫 번째 만 필요한 경우 옵션 -f을 사용할 수 있습니다.


이 시점에서 우리는 다른 목표에 빠진다.

쉘이 실행할 것 (구문 분석 후)

그것은 두 번째 문장에서 나옵니다.

유닉스 쉘에 명령 이름을 입력하면 어떻게되는지 확인

이 두 번째 주제는 대답하기 어려운 질문에 대한 정답을 찾으려고합니다. 쉘에는 다양한 견해, 코너 케이스 및 (최소한) 다른 해석이 있습니다. 그것에 추가 :

유형, 명령, 위치, 위치, 위치, 위치, 해시 등 다양한 유틸리티가 있습니다.

그리고 모든 시도는 그 목표와 일치합니다.


어느 것을 피합니까?

우리는 종종 피해야 할 것을 듣습니다.

궁금하다 : 왜 which(적어도 데비안에서는) 제대로 작동 한다면 그렇게 말해야 하는가?

유닉스 정신에서 : 각 프로그램이 한 가지 일을 잘 수행하게하십시오.

외부 프로그램은 which하나의 일을하고 다음과 같은 이름을 가진 PATH의 첫 번째 실행 파일 찾기 명령 이름을 . 그리고 합리적으로 잘하고 있습니다.

더 근본적인 방법 으로이 질문에 대답하는 다른 프로그램이나 유틸리티를 모르겠습니다. 따라서 유용하고 필요할 때 사용할 수 있습니다.

가장 가까운 대안은 다음 command -pv commandName과 같습니다 .하지만 내장 및 별칭에 대해서도보고합니다. 같은 대답이 아닙니다.

물론, which제한적이며 모든 질문에 대답하지는 못하지만 어떤 도구도 그렇게 할 수는 없습니다 (아직은 ...). 그러나 답변하도록 설계된 질문 (위의 질문)에 답변 할 때 유용합니다. 거의 ed제한적이었고 sed등장했다 (또는 vi/ vim). 또는 이와 같이 awk제한되어 Perl이 나타나고 확장되었습니다. 그러나 ed, sed및 / 또는 awk위치를 특정 사용 케이스가 vimperl있습니다 하지 최고의 도구가.

왜?

아마도 which쉘 사용자가 물어볼 수있는 질문의 일부에만 대답 하기 때문일 것입니다.

commandName을 입력하면 어떻게됩니까?


외부

외부 실행 파일로 많은 시스템에서 사용할 수 있어야합니다.
외부 도구를 호출하는 유일한 확실한 방법은 env를 사용하여 셸에서 나가서 which(모든 셸에서 작동) 호출 하는 것입니다.

 $ env which which
 /usr/bin/which

또는 전체 경로를 사용하십시오 which(시스템마다 다를 수 있음).

 /usr/bin/which which 

hack필요한가요? 일부 쉘 (특히 zsh)은 다음과 같이 숨 깁니다 which.

 $ zsh -c 'which which'
 which: shell built-in command

외부 도구 (같은 것은 env) 완벽하게 쉘 내부 정보를보고하지 않습니다 이유를 설명. 별명, 함수, 내장, 특수 내장, 쉘 (내보내기되지 않은) 변수 등 :

 $ env which ls
 /usr/bin/ls
 $ env which ll       # empty output

ll(의 공통 별칭 ll='ls -l') 의 빈 출력은 ll실행 프로그램과 관련이 없거나 적어도 llPATH에 이름이 지정된 실행 파일이 없음을 나타냅니다 . 를 사용하면 ll다른 것을 호출해야합니다 (이 경우 별명).

 $ type ll
 ll is aliased to `ls -l'

typecommand

명령 typecommand -vPOSIX에서 요청합니다. 그것들은 대부분의 쉘에서 작동해야하며 csh, tcsh, fish 및 rc를 제외하고는 작동합니다.

두 명령 모두 명령이 실행될 다른 관점을 제공하는 데 사용될 수 있습니다.

whence, where, whereis, whatis,hash

그런 다음, 거기에 whence, where, whereis, whatis, hash, 일부 다른 사람. 비슷한 질문에 대한 모든 다른 답변. 모든 쉘에서 다른 방식으로 작동합니다. 아마, whence이후에 가장 흔 type합니다. 다른 것들은 다른 방법으로 같은 질문에 대답하는 특별한 해결책입니다.

대신 무엇을 사용해야합니까?

아마 which의 이름으로 실행 파일이 존재하는 경우 먼저 알고 커맨드 이름 다음 typecommand경우, 다음 커맨드 이름은 : 아직 발견되지 않은 whence, where, whereis, whatis, hash순서대로.


which실행 파일 을 제공하는 셸 스크립트

#! /bin/sh
set -ef; oldIFS=$IFS; IFS=:

say()( IFS=" "; printf "%s\n" "$*"; )
say "Simplified version of which."
usage(){ say Usage: "$0" [-f] args; }
if [ "$#" -eq 0 ]; then say Missing argument(s); usage; exit 2; fi

firstmatch=0
while getopts f whichopts; do
    case "$whichopts" in
        f) firstmatch=1 ;;
        ?) usage; exit 3 ;;
    esac
done
[ "$OPTIND" -gt 1 ] && shift `expr "$OPTIND" - 1`

allret=0; [ "$#" -eq 0 ] && allret=1
for program in "$@"; do
    ret=1
    for element in $PATH''; do
        case "$program" in
            */*) element="$program"; loop=0;;
            *)   element="${element:-.}/$program"; loop=1;;
        esac
        if [ -f "$element" ] && [ -x "$element" ]; then
            say "$element"
            ret=0
            if [ "$firstmatch" -eq 1 ] || [ "$loop" -eq 0 ]; then break; fi
        fi
    done
    [ "$ret" -eq 1 ] && allret=1
done

IFS="$oldIFS"
exit "$allret"

0

우리는 종종 피해야 할 것을 듣습니다. 왜? 대신 무엇을 사용해야합니까?

나는 그것을들은 적이 없다. 구체적인 예를 제시하십시오. 나는 리눅스 배포판과 설치된 소프트웨어 패키지에 대해 걱정할 which것입니다.

SLES 11.4 x86-64

tcsh 버전 6.18.01에서 :

> which which

which: shell built-in command.

bash 버전 3.2-147에서 :

> which which

/usr/bin/which

> which -v

GNU which v2.19, Copyright (C) 1999 - 2008 Carlo Wood.
GNU which comes with ABSOLUTELY NO WARRANTY;
This program is free software; your freedom to use, change
and distribute this program is protected by the GPL.

which Linux 운영 체제의 일부로 사용하기 위해 Linux Kernel Organization에서 배포 한 표준 패키지 인 util-linux 의 일부입니다. 이 다른 파일들도 제공합니다

/bin/dmesg
/bin/findmnt
/bin/logger
/bin/lsblk
/bin/more
/bin/mount
/bin/umount
/sbin/adjtimex
/sbin/agetty
/sbin/blkid
/sbin/blockdev
/sbin/cfdisk
/sbin/chcpu
/sbin/ctrlaltdel
/sbin/elvtune
/sbin/fdisk
/sbin/findfs
/sbin/fsck
/sbin/fsck.cramfs
/sbin/fsck.minix
/sbin/fsfreeze
/sbin/fstrim
/sbin/hwclock
/sbin/losetup
/sbin/mkfs
/sbin/mkfs.bfs
/sbin/mkfs.cramfs
/sbin/mkfs.minix
/sbin/mkswap
/sbin/nologin
/sbin/pivot_root
/sbin/raw
/sbin/sfdisk
/sbin/swaplabel
/sbin/swapoff
/sbin/swapon
/sbin/switch_root
/sbin/wipefs
/usr/bin/cal
/usr/bin/chrp-addnote
/usr/bin/chrt
/usr/bin/col
/usr/bin/colcrt
/usr/bin/colrm
/usr/bin/column
/usr/bin/cytune
/usr/bin/ddate
/usr/bin/fallocate
/usr/bin/flock
/usr/bin/getopt
/usr/bin/hexdump
/usr/bin/i386
/usr/bin/ionice
/usr/bin/ipcmk
/usr/bin/ipcrm
/usr/bin/ipcs
/usr/bin/isosize
/usr/bin/line
/usr/bin/linux32
/usr/bin/linux64
/usr/bin/look
/usr/bin/lscpu
/usr/bin/mcookie
/usr/bin/mesg
/usr/bin/mkzimage_cmdline
/usr/bin/namei
/usr/bin/rename
/usr/bin/renice
/usr/bin/rev
/usr/bin/script
/usr/bin/scriptreplay
/usr/bin/setarch
/usr/bin/setsid
/usr/bin/setterm
/usr/bin/tailf
/usr/bin/taskset
/usr/bin/time
/usr/bin/ul
/usr/bin/uname26
/usr/bin/unshare
/usr/bin/uuidgen
/usr/bin/wall
/usr/bin/whereis
/usr/bin/which
/usr/bin/write
/usr/bin/x86_64
/usr/sbin/addpart
/usr/sbin/delpart
/usr/sbin/fdformat
/usr/sbin/flushb
/usr/sbin/freeramdisk
/usr/sbin/klogconsole
/usr/sbin/ldattach
/usr/sbin/partx
/usr/sbin/rcraw
/usr/sbin/readprofile
/usr/sbin/rtcwake
/usr/sbin/setctsid
/usr/sbin/tunelp

util-linux버전은 2.19입니다. 릴리스 노트는 2007 년 8 월 28 일자 v2.13으로 쉽게 찾을 수 있습니다. 이것의 요점이나 목표가 무엇인지 확실하지 않은 것은 확실히 331 번 올라간 그 긴 일에 대해서는 대답하지 않았다.


2
질문에서 유닉스가 무엇을 말하는지 언급하지 않는 방법에 주목하십시오. 리눅스는 몇 가지 중 하나 일뿐입니다.
Kusalananda

2
당신의 which -v쇼 처럼 , 그것은 AFAIK가 결코 which유틸리티를 포함하지 않은 util-linux가 아닌 GNU입니다 (다른 답변에서 언급하고 사치스럽지 않은 리눅스에 대한 것 입니다). util-linux 2.19는 2011 년, GNU 2.19는 2008 년입니다.
Stéphane Chazelas
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.