Bash 스크립트에서 프로그램이 있는지 어떻게 확인할 수 있습니까?


2209

오류를 반환하고 종료하거나 스크립트를 계속 진행하는 방식으로 프로그램이 존재하는지 어떻게 확인합니까?

쉬워야하는 것처럼 보이지만 저를 혼란스럽게하고 있습니다.


"프로그램"이란 무엇입니까? 함수와 별칭이 포함되어 있습니까? which이것에 대해 true를 반환합니다. type인수가 없으면 예약어 및 쉘 내장에 대해 추가로 true를 리턴합니다. "program"이 "excutable in"을 의미하는 경우이 답변$PATH 을 참조하십시오 .
Tom Hale

답변:


3050

대답

POSIX 호환 :

command -v <the_command>

Bash 특정 환경의 경우 :

hash <the_command> # For regular commands. Or...
type <the_command> # To check built-ins and keywords

설명

피하십시오 which. 뿐만 아니라 그것은 (의미 내장 명령이 좋아 아주 작은 일에있는 거 발사 외부 프로세스 당신이다 hash, type또는 command방법 저렴) 외부 명령의 효과를 쉽게 따라 다를 수 있지만, 당신은 또한, 실제로 당신이 원하는 것을 할 수있는 내장 명령에 의존 할 수 시스템에서 시스템으로.

왜 신경써?

  • 많은 운영 체제에는 종료 상태를 설정하지도 않는 이 있습니다. which즉 , 작동하지 않으며 작동하지 않더라도 항상 존재한다고 보고합니다 (일부 POSIX 셸 도이 작업을 수행하는 것처럼 보입니다 ).if which foofoohash
  • 많은 운영 체제 which에서 출력을 변경하거나 패키지 관리자에 연결하는 등 사용자 지정 및 악의적 인 작업을 수행합니다.

따라서을 사용하지 마십시오 which. 대신 다음 중 하나를 사용하십시오.

$ command -v foo >/dev/null 2>&1 || { echo >&2 "I require foo but it's not installed.  Aborting."; exit 1; }
$ type foo >/dev/null 2>&1 || { echo >&2 "I require foo but it's not installed.  Aborting."; exit 1; }
$ hash foo 2>/dev/null || { echo >&2 "I require foo but it's not installed.  Aborting."; exit 1; }

(사소한 참고 사항 : 일부는 2>&-동일 2>/dev/null하지만 짧을 것을 제안 합니다 - 이것은 사실이 아닙니다 . 2>&-FD 2를 닫아 stderr에 쓰려고 할 때 프로그램 오류 가 발생합니다 . 이는 성공적으로 쓰거나 출력을 버리는 것과는 매우 다릅니다 (그리고 위험하다!))

해시 뱅이라면 /bin/shPOSIX의 말에 신경을 써야합니다. typehash의 종료 코드가 정말 잘 POSIX에 의해 정의되지 않으며, hash명령이 존재하지 않는 경우 성공적으로 종료 볼 수있다 (이것을 보지 못했어요 type아직). command의 종료 상태는 POSIX에 의해 잘 정의되어 있으므로 사용하기에 가장 안전합니다.

스크립트를 사용하는 경우 bash그러나, POSIX 규칙은 문제가 더 이상 정말 안 모두 typehash사용에 완벽하게 안전됩니다. type이제이 있습니다 -P단지를 검색 PATH하고 hash명령의 위치가 아마 당신은 실제로하기 위해 그 존재를 확인을 사용하기 때문에 일반적으로 좋은 일이다, (빠른 조회 다음에 당신이 그것을 사용하는 경우) 해시됩니다 부작용이있다 .

간단한 예로, gdate존재하는 경우 실행되는 함수가 있습니다 date.

gnudate() {
    if hash gdate 2>/dev/null; then
        gdate "$@"
    else
        date "$@"
    fi
}

35
@Geert : &> / dev / null 부분은 'foo'가 존재하지 않을 때 'type'메시지를 숨 깁니다. 에코의> & 2는 표준 출력 대신 표준 오류로 오류 메시지를 전송합니다. 그것이 컨벤션이기 때문입니다. 둘 다 터미널에 나타나지만 표준 오류는 오류 메시지와 예기치 않은 경고에 대한 기본 출력입니다.
lhunath

5
-P 플래그는 'sh'에서 작동하지 않습니다 (예 : stackoverflow.com/questions/2608688/…
momeara

130
bash의 '고급'i / o 리디렉션에 익숙하지 않은 사용자의 경우 : 1) 2>&- ( "stderr 인"출력 파일 디스크립터 2 닫기 ")2> /dev/null; 2) >&2는에 대한 바로 가기이며 1>&2"stdout을 stderr로 리디렉션"으로 인식 할 수 있습니다. 자세한 내용은 Advanced Bash 스크립팅 안내서 I / O 리디렉션 페이지 를 참조하십시오.
mikewaters

9
@mikewaters ABS는 상당히 발전된 것으로 보이며 광범위한 bash 및 비 -bash CLI 기능을 설명하지만 많은 측면에서 매우 소홀하며 모범 사례를 따르지 않습니다. 이 의견에는 글을 쓸 공간이 거의 없습니다. 하지만 난 나쁜 코드 몇 임의 예를 붙여 넣을 수 있습니다 : while read element ; do .. done <<< $(echo ${ArrayVar[*]}), for word in $(fgrep -l $ORIGINAL *.txt), ls -l "$directory" | sed 1d , {{의에서 seq $BEGIN $END}}, ... 많은 사람들이 저자에게 연락하고 개선을 제안하는 시도했지만이 더 위키 없습니다 및 요청 귀머거리 귀에 착륙했다.
lhunath

56
@mikewaters가 2>&-있다 하지 동일 2>/dev/null. 전자는 파일 디스크립터를 닫고 후자는 파일 디스크립터를로 리디렉션합니다 /dev/null. 프로그램이 stderr이 닫 혔음을 stderr에 알리려고 시도하기 때문에 오류가 표시되지 않을 수 있습니다.
nyuszika7 시간

575

다음은 명령이 존재 $PATH 하고 실행 가능한지 확인하는 이식 가능한 방법 입니다.

[ -x "$(command -v foo)" ]

예:

if ! [ -x "$(command -v git)" ]; then
  echo 'Error: git is not installed.' >&2
  exit 1
fi

해당 이름의 실행 파일이 없으면 bash에서 실행 파일이 아닌 파일을 반환하므로 실행 파일 검사가 필요합니다 $PATH.

실행 파일과 이름이 같은 실행 파일이 아닌 파일이 이전에 파일이 있으면 $PATH대시 파일이 실행 되더라도 대시가 전자를 반환합니다. 이것은 버그이며 POSIX 표준을 위반합니다. [ 버그 리포트 ] [ 표준 ]

또한 찾고있는 명령이 별명으로 정의 된 경우 실패합니다.


4
command -v심지어 비 실행 파일의 경로를 생성? 즉, -x가 정말로 필요합니까?
einpoklum

5
@einpoklum -x은 파일이 실행 가능한지 테스트합니다.
Ken Sharp

3
@ KenSharp : 그러나 그 command자체는 실행 가능한지 테스트하기 때문에 중복되는 것 같습니다 .
einpoklum

13
@einpoklum 예, 필요합니다. 실제로,이 솔루션조차도 한 가지 경우에 깨질 수 있습니다. 관심을 가져 주셔서 감사합니다. $PATH명령을 실행할 때 dash, bash 및 zsh는 모두 실행 파일이 아닌 파일을 건너 뜁니다 . 그러나 동작 command -v이 매우 일치하지 않습니다. 대시에서는 $PATH실행 가능 여부에 관계없이 에서 첫 번째로 일치하는 파일을 반환합니다 . bash에서는에서 첫 번째 실행 파일 일치를 반환 $PATH하지만 없으면 실행 파일이 아닌 파일을 반환 할 수 있습니다. 그리고 zsh에서는 실행 파일이 아닌 파일을 반환하지 않습니다.
nyuszika7 시간

4
내가 알 수있는 한, dashPOSIX를 준수하지 않는 세 가지 중에서 유일한 것입니다. [ -x "$(command -v COMMANDNAME)"]다른 두 가지에서 작동합니다. 이 버그는 이미보고되었지만 아직 응답이없는 것 같습니다 : bugs.debian.org/cgi-bin/bugreport.cgi?bug=874264
nyuszika7h

210

나는 lhunath의 사용을 권장하지 않으며 which, 그의 솔루션은 Bash 사용자에게 완벽하게 유효 합니다 . 그러나보다 휴대 command -v하기 쉽도록 다음을 사용해야합니다.

$ command -v foo >/dev/null 2>&1 || { echo "I require foo but it's not installed.  Aborting." >&2; exit 1; }

명령 command은 POSIX를 준수합니다. 사양은 여기를 참조하십시오 : command-간단한 명령을 실행합니다

참고 : typePOSIX를 준수하지만 type -P그렇지 않습니다.


3
위와 동일 exit 1;-xterm을 호출하면 xterm을 종료합니다.
사용자 알 수 없음

1
이것은 표준 sh에서 작동하지 않습니다. & &는 유효한 리디렉션 지침이 아닙니다.
jyavenard

7
@ jyavenard : 질문에 bash 태그가 붙어 있으므로 더 정확한 bash 특정 리디렉션 표기법이 사용 &>/dev/null됩니다. 그러나, 나는 당신에게 동의합니다. 실제로 중요한 것은 이식성 >/dev/null 2>&1입니다. 이제 표준 sh redirect를 사용하여 그에 따라 답변을 편집했습니다 .
GregV

이 답변을 더 향상시키기 위해 두 가지 작업을 수행합니다. 1 : Josh의 답변과 같이 "&>"를 사용하여 단순화합니다. 2 : 가독성을 위해 에코 앞에 탭을 추가하여 {}를 추가 줄로
나누십시오.

사람이 ... 그것을 원한다면 난 그냥 bash는 함수에이 하나의 라이너를 넣어 github.com/equant/my_bash_tools/blob/master/tarp.bash
콴트

94

내 .bashrc 에이 기능을 쉽게 정의 할 수있는 함수가 있습니다.

command_exists () {
    type "$1" &> /dev/null ;
}

다음은 어떻게 사용되는지에 대한 예입니다 (my에서 .bash_profile.)

if command_exists mvim ; then
    export VISUAL="mvim --nofork"
fi

무엇을 &>합니까?
Saad Malik


&>Bash 버전에서 사용하지 못할 수 있습니다. Marcello의 코드는 정상적으로 작동합니다. 그것은 같은 일을합니다.
Josh Strater 1

3
내장 및 예약어 실패 : 예를 들어이 단어를 사용해보십시오 then. 에 실행 파일이 필요하면 이 답변을 참조하십시오 $PATH.
Tom Hale

84

$PATH변수 의 디렉토리 중 하나에 존재 하는지 또는 절대 위치 를 알고 있는지 여부에 따라 다릅니다 . $PATH변수 에 있는지 알고 싶다면

if which programname >/dev/null; then
    echo exists
else
    echo does not exist
fi

그렇지 않으면 사용

if [ -x /path/to/programname ]; then
    echo exists
else
    echo does not exist
fi

/dev/null/첫 번째 예에서 방향 재 지정 은 which프로그램 의 출력을 억제합니다 .


22
내 의견에 요약 된 이유로 "어떤"을 사용해서는 안됩니다.
lhunath

39

@lhunath와 @GregV의 답변을 확장하여 다음과 같이 수표를 쉽게 if명세서에 넣기를 원하는 사람들을위한 코드가 있습니다 .

exists()
{
  command -v "$1" >/dev/null 2>&1
}

사용 방법은 다음과 같습니다.

if exists bash; then
  echo 'Bash exists!'
else
  echo 'Your system does not have Bash'
fi

13
배우고 개선하려는 의지는 보상되어야합니다. +1 깨끗하고 간단합니다. 내가 추가 할 수있는 유일한 것은 command별칭에 대해서도 성공 한다는 것 입니다. 이는 직관적이지 않을 수도 있습니다. 대화식 쉘에 존재하는지 확인하면 스크립트로 옮길 때와 다른 결과가 나타납니다.
Palec

1
방금 테스트하고 사용 shopt -u expand_aliases하면 별칭 / alias ls='ls -F'다른 답변 에서 언급 한 것처럼 무시 / 숨기기를 사용하고를 통해 별칭을 shopt -s expand_aliases해결합니다 command -v. 따라서 명령 호출의 출력을 명시 적으로 캡처하고 반환하지 않으면 함수 반환 값에 영향을 줄 수 있지만 검사 전에 설정하고 나중에 설정 해제해야합니다.
dragon788

24

다음을 사용하십시오.

test -x filename

또는

[ -x filename ]

조건식 아래의 Bash 맨 페이지에서 :

 -x file
          True if file exists and is executable.

26
즉, 응용 프로그램의 전체 경로를 이미 알고 있어야합니다.
lhunath

12
OP는 특정 인스턴스 또는 실행 가능한 인스턴스를 확인 하려는지 여부를 지정하지 않았습니다 ... 읽은 방식으로 대답했습니다.
dmckee --- 전

16

사용하려면 hash, @lhunath에서 알 수 있듯이 배쉬 스크립트 :

hash foo &> /dev/null
if [ $? -eq 1 ]; then
    echo >&2 "foo not found."
fi

이 스크립트는 실행 된 hash다음 가장 최근 명령의 종료 코드 (에 저장된 값 $?)가 같은지 확인합니다 1. 를 hash찾지 못하면 foo종료 코드는입니다 1. 이 있으면 foo종료 코드는입니다 0.

&> /dev/null화면에 나타나지 않도록 표준 오류표준 출력 을 리디렉션 하고 메시지를 표준 오류에 씁니다.hashecho >&2


8
왜 안돼 if hash foo &> /dev/null; then ...?
Beni Cherniavsky-Paskin

9

내가 액세스 할 수있는 상자에서 이전 답변을 얻지 못했습니다. 하나, type설치되었습니다 (무엇을 more수행). 따라서 내장 지시문이 필요합니다. 이 명령은 저에게 효과적입니다.

if [ `builtin type -p vim` ]; then echo "TRUE"; else echo "FALSE"; fi

3
대괄호는 if구문의 일부가 아니므로 간단히 사용하십시오 if builtin type -p vim; then .... 그리고 백틱은 실제로 고대와 더 이상 사용되지 않는 구문이며 모든 최신 시스템 $()에서도 지원 sh됩니다.
nyuszika7 시간

9

여러 종속성을 확인하고 최종 사용자에게 상태를 알리십시오.

for cmd in latex pandoc; do
  printf '%-10s' "$cmd"
  if hash "$cmd" 2>/dev/null; then
    echo OK
  else
    echo missing
  fi
done

샘플 출력 :

latex     OK
pandoc    missing

10를 최대 명령 길이로 조정하십시오 . 자세한 POSIX 방법이 보이지 않기 때문에 자동이 아닙니다 .Bash에서 공백으로 구분 된 테이블의 열을 어떻게 정렬 할 수 있습니까?

일부 apt패키지가 함께 설치되어 있는지 확인 dpkg -s하고 그렇지 않으면 설치하십시오 .

: 참조 apt-get을 패키지가 설치되어 있는지 확인하고 리눅스 아니라면 다음 설치

: 그것은 이전에 언급 한 프로그램이 배쉬 스크립트에서 존재하는 경우 내가 확인할 수 있습니까?


1
비 자세한 방법으로 1) 너비 지정자를 제거하십시오. 2) 명령 이름의 printf 뒤에 공백을 추가하십시오. 3) for 루프를 column -t(util-linux의 일부 )에 파이프하십시오 .
파트리스 레베 스크

8

프로그램이 있는지 확인하면 나중에 실행하게 될 것입니다. 처음부터 실행 해 보지 않겠습니까?

if foo --version >/dev/null 2>&1; then
    echo Found
else
    echo Not found
fi

단순히 PATH 디렉토리와 파일 권한을 보는 것보다 프로그램을 실행하는 것이 더 신뢰할만한 검사입니다.

또한 버전과 같은 프로그램에서 유용한 결과를 얻을 수 있습니다.

물론 단점은 일부 프로그램이 시작하기에 무거울 수 있고 일부 프로그램은 --version즉시 (그리고 성공적으로) 종료 할 수있는 옵션 이 없다는 것 입니다.


6

hash foo 2>/dev/null: Z shell (Zsh), Bash, Dashash 와 함께 작동합니다 .

type -p foo: Z 쉘, Bash 및 ash ( BusyBox )에서는 작동하지만 Dash에서는 작동하지 않는 것으로 보입니다 ( -p인수로 해석 됨).

command -v foo: Z shell, Bash, Dash와 함께 작동하지만 ash (BusyBox) ( -ash: command: not found) 에서는 작동하지 않습니다 .

또한 builtinash 및 Dash에는 제공되지 않습니다.


4

가능한 경우 Bash 내장을 사용하십시오.

which programname

...

type -P programname

15
응? whichBash 내장이 아닙니다.
tripleee

-P programname 유형이 선호됩니다. 허용 된 답변 참조
RobertG

@RobertG 내가 보는 것은 -PPOSIX가 아닙니다. 왜 type -P선호됩니까?
mikemaccana

나는 배쉬 특정의 이전 코멘트에 대답하고자하는 것처럼 "배쉬 환경에서 선호된다"고 말해야했다. 어쨌든, 그것은 몇 년 전입니다. 다시 한 번 말씀 드리지만 "잠복"으로 표시된 답변을 알려 주셔야합니다
RobertG

4

-vPOSIX_BUILTINS 옵션이 <command>테스트 대상으로 설정되어 있으면 명령 이 제대로 작동 하지만 그렇지 않으면 실패 할 수 있습니다. (수년 동안 저에게 효과가 있었지만 최근에는 작동하지 않는 곳에 부딪 쳤습니다.)

나는 다음이 더 내결함성이 있음을 발견했다.

test -x $(which <command>)

경로, 존재 및 실행 권한의 세 가지를 테스트하기 때문에.


작동하지 않습니다. 설치되어 실행 가능하지만 내가 실행중인 도커 컨테이너 내에 설치되어 있지 않더라도 test -x $(which ls)0을 반환합니다 .test -x $(which sudo)lssudo
algal

@algal 내가 생각하는 인용 부호를 사용해야합니다.test -x "$(which <command>)"
JoniVR

@algal 아마도 ls별칭이 있습니까? 명령에 매개 변수가 있으면 작동하지 않는다고 생각합니다.
AnthonyC

3

관심있는 사람들을 위해 설치된 라이브러리를 감지하려는 경우 이전 답변의 방법 중 어느 것도 작동하지 않습니다. 경로를 물리적으로 검사하거나 (헤더 파일 등의 가능성이있는) 또는 이와 비슷한 것 (Debian 기반 배포판 인 경우)이 남아 있다고 생각합니다.

dpkg --status libdb-dev | grep -q not-installed

if [ $? -eq 0 ]; then
    apt-get install libdb-dev
fi

위에서 볼 수 있듯이 쿼리에서 "0"응답은 패키지가 설치되지 않았 음을 의미합니다. 이것은 "grep"의 기능입니다. "0"은 일치 항목이 발견되었음을 의미하고 "1"은 일치 항목이 발견되지 않음을 의미합니다.


10
그러나 안티 패턴 cmd; if [ $? -eq 0 ]; then은 다음과 같이 리팩토링되어야합니다.if cmd; then
tripleee

이 단지를 통해 설치 libs와 작동 dpkg또는apt
Weijun 저우

3

여기에는 많은 옵션이 있지만 빠른 원 라이너가 없다는 것에 놀랐습니다. 이것은 스크립트 시작 부분에서 사용한 것입니다.

[[ "$(command -v mvn)" ]] || { echo "mvn is not installed" 1>&2 ; exit 1; }
[[ "$(command -v java)" ]] || { echo "java is not installed" 1>&2 ; exit 1; }

이것은 선택한 답변과 다른 출처를 기반으로합니다.


2

매달려있는 aliasES 로 인해 휴대용 및 100 % 신뢰할 수있는 방법이 없다고 말하고 싶습니다 . 예를 들면 다음과 같습니다.

alias john='ls --color'
alias paul='george -F'
alias george='ls -h'
alias ringo=/

물론, 마지막 것만 문제가 있습니다 (Ringo에 대한 위반은 없습니다!). 그러나 그들 모두는 alias의 관점에서 유효합니다 command -v.

와 같이 매달려있는 것을 거부 ringo하려면 쉘 내장 alias명령 의 출력을 구문 분석 하고 재귀해야합니다 ( 여기서는 command -v우월하지 않습니다 alias).이를위한 휴대용 솔루션은 없으며 심지어 Bash- 특정 솔루션은 다소 지루합니다.

이와 같은 솔루션은 무조건 거부합니다 alias ls='ls -F'.

test() { command -v $1 | grep -qv alias }

좋은 지적. 그러나 bash 스크립트 내부에서 실행할 때는 별명이 표시되지 않습니다.
Basil Musa

1
또한 'alias'명령이 확인되면 문제가 발생합니다. 그것이 true를 반환해야 할 때. 예 : "별칭"테스트
Basil Musa

2
방금 테스트하고 사용 shopt -u expand_aliases하면 이러한 별칭 을 무시 / 숨기고 shopt -s expand_aliases를 통해 표시합니다 command -v.
dragon788

2

프로그램이 존재하는지 여부에 따라 위치에 따라 알려줍니다.

    if [ -x /usr/bin/yum ]; then
        echo "This is Centos"
    fi

네, sevrer, Open suse, centos, Debian에 패키지를 설치해야한다면이 명령을 추가했습니다
Klevin Kona

구문 강조는 "echo"행에서 꺼져 있습니다. 해결 방법이 무엇입니까? Bash 스크립트가 달라야한다고 제안합니까?
Peter Mortensen

@PeterMortensen 구문 강조 표시는 문자열임을 인식하지 못하므로 해제되어 있습니다.
Adrien

1

which명령이 유용 할 수 있습니다. 어떤 남자

실행 파일이 발견되면 0을, 실행 파일이 없거나 실행 파일이 아닌 경우 1을 리턴합니다.

NAME

       which - locate a command

SYNOPSIS

       which [-a] filename ...

DESCRIPTION

       which returns the pathnames of the files which would
       be executed in the current environment, had its
       arguments been given as commands in a strictly
       POSIX-conformant shell. It does this by searching
       the PATH for executable files matching the names
       of the arguments.

OPTIONS

       -a     print all matching pathnames of each argument

EXIT STATUS

       0      if all specified commands are 
              found and executable

       1      if one or more specified commands is nonexistent
              or not executable

       2      if an invalid option is specified

좋은 점은 which실행 파일이 실행되는 환경에서 사용 가능한지 알아내는 것입니다 which. 몇 가지 문제가 발생하지 않습니다.


foo라는 실행 파일을 찾고 있지만 특정 파일 / path / to / a / named / foo를 확인하려면 내 대답을 참조하십시오. 또한 ...이 모든 전체 본격적인 설치에 존재해야하지만, 최소한의 시스템에서 사용하지 못할 수있는 점에 유의
dmckee --- 전 사회자 고양이

9
종료 상태에 의존하지 마십시오. 많은 운영 체제는 0 이외의 종료 상태를 설정하지도 않습니다.
lhunath

1

데비안 서버 설정 :

여러 패키지에 동일한 이름이 포함되어있을 때 문제가 발생했습니다.

예를 들면 apache2. 그래서 이것은 내 해결책이었습니다.

function _apt_install() {
    apt-get install -y $1 > /dev/null
}

function _apt_install_norecommends() {
    apt-get install -y --no-install-recommends $1 > /dev/null
}
function _apt_available() {
    if [ `apt-cache search $1 | grep -o "$1" | uniq | wc -l` = "1" ]; then
        echo "Package is available : $1"
        PACKAGE_INSTALL="1"
    else
        echo "Package $1 is NOT available for install"
        echo  "We can not continue without this package..."
        echo  "Exitting now.."
        exit 0
    fi
}
function _package_install {
    _apt_available $1
    if [ "${PACKAGE_INSTALL}" = "1" ]; then
        if [ "$(dpkg-query -l $1 | tail -n1 | cut -c1-2)" = "ii" ]; then
             echo  "package is already_installed: $1"
        else
            echo  "installing package : $1, please wait.."
            _apt_install $1
            sleep 0.5
        fi
    fi
}

function _package_install_no_recommends {
    _apt_available $1
    if [ "${PACKAGE_INSTALL}" = "1" ]; then
        if [ "$(dpkg-query -l $1 | tail -n1 | cut -c1-2)" = "ii" ]; then
             echo  "package is already_installed: $1"
        else
            echo  "installing package : $1, please wait.."
            _apt_install_norecommends $1
            sleep 0.5
        fi
    fi
}

1

당신 / 녀석이 여기에있는 답변을 얻지 못하고 머리카락을 등에서 빼내는 경우을 사용하여 동일한 명령을 실행하십시오 bash -c. 이 somnambular 섬망을보십시오. 이것은 $ (sub-command)를 실행할 때 실제로 일어나는 일입니다.

먼저. 완전히 다른 출력을 줄 수 있습니다.

$ command -v ls
alias ls='ls --color=auto'
$ bash -c "command -v ls"
/bin/ls

둘째. 전혀 출력을 줄 수 없습니다.

$ command -v nvm
nvm
$ bash -c "command -v nvm"
$ bash -c "nvm --help"
bash: nvm: command not found

쉘의 대화식 모드와 비 대화식 모드의 차이로 인해 차이가 발생합니다. ~ / .bashrc는 쉘이 비 로그인이고 대화식 일 때만 읽습니다. 두 번째 것은 PATH 환경 변수의 차이로 인해 발생해야하지만 서브 쉘은 환경을 상속하기 때문에 이상하게 보입니다.
Palec

내 경우에는 .bashrcA는이 [ -z "$PS1" ] && return앞에 붙은 # If not running interactively, don't do anything내가 그 비 대화식 모드에서 bashrc에조차 명시 적 소싱 도움이되지 않는 이유입니다 생각 때문에. ss64.com/bash/source.html 도트 연산자를 사용하여 스크립트를 호출하면이 문제를 해결할 수 . ./script.sh있지만 매번 입력해야하는 것은 아닙니다.
user619271

1
소싱되지 않은 소싱 스크립트는 나쁜 생각입니다. 내가 말하려는 것은 귀하의 답변이 묻는 질문과 거의 관련이 없으며 Bash 및 (비) 대화 형 모드와 관련이 있다는 것입니다.
Palec

이 경우에 무슨 일이 일어나고 있는지 설명하면 답에 도움이되는 부록이 될 것입니다.
Palec

0

해시 변형에는 하나의 함정이 있습니다. 명령 행에서 예를 들어

one_folder/process

프로세스가 실행되도록합니다. 이를 위해 one_folder의 상위 폴더는 $ PATH에 있어야합니다 . 그러나이 명령을 해시하려고하면 항상 성공합니다.

hash one_folder/process; echo $? # will always output '0'

4
"이를 위해 one_folder의 상위 폴더는 $PATH"에 있어야합니다 . — 이것은 정확하지 않습니다. 시도 해봐. 이것이 작동하려면 one_folder가 현재 디렉토리 에 있어야합니다 .
와일드 카드

0

두 번째로 "command -v"명령을 사용했습니다. 예를 들면 다음과 같습니다.

md=$(command -v mkdirhier) ; alias md=${md:=mkdir}  # bash

emacs="$(command -v emacs) -nw" || emacs=nano
alias e=$emacs
[[ -z $(command -v jed) ]] && alias jed=$emacs

0

CI 서버 배포의 일부로 Git이 설치되어 있는지 확인해야했습니다 . 마지막 Bash 스크립트는 다음과 같습니다 (Ubuntu 서버).

if ! builtin type -p git &>/dev/null; then
  sudo apt-get -y install git-core
fi

3
조건은 다소 쓸모가 없으며 apt-get이 충족되고 git-core가 이미 설치된 경우 종료되므로 apt-get을 실행하는 시작 시간을 모듈로합니다.
tripleee

3
시작 시간은 무시할 수 없지만 더 중요한 동기는 sudo조건부 가 없으면 항상 중지하고 암호를 묻는 것입니다 (최근에 sudo를 수행하지 않은 경우). BTW, sudo -p "Type your password to install missing git-core: "프롬프트가 파란색에서 나오지 않도록하는 것이 유용 할 수 있습니다 .
Beni Cherniavsky-Paskin

0

Bash를 모방하기 위해 type -P cmdPOSIX 호환을 사용할 수 있습니다 env -i type cmd 1>/dev/null 2>&1.

man env
# "The option '-i' causes env to completely ignore the environment it inherits."
# In other words, there are no aliases or functions to be looked up by the type command.

ls() { echo 'Hello, world!'; }

ls
type ls
env -i type ls

cmd=ls
cmd=lsx
env -i type $cmd 1>/dev/null 2>&1 || { echo "$cmd not found"; exit 1; }

7
이것이 왜 상향 조정되고 있습니까? 실제로 어떤 시스템에서 작동합니까? typeA는 것 같다 builtin이 때문에 작동하지 않을 수 있습니다 대부분의 쉘에서 env사용이 execvp실행할 수 command있도록 command수 없습니다 builtin(그리고이 builtin항상 같은 환경에서 실행됩니다). 이것은 나를 실패 bash, ksh93, zsh, busybox [a]shdash제공 모두 type쉘 내장한다.
Adrian Frühwirth

0

type사용 가능한 외부 명령 이없는 경우 ( 여기 에 부여 된대로 ) POSIX 호환을 사용할 수 있습니다 env -i sh -c 'type cmd 1>/dev/null 2>&1'.

# Portable version of Bash's type -P cmd (without output on stdout)
typep() {
   command -p env -i PATH="$PATH" sh -c '
      export LC_ALL=C LANG=C
      cmd="$1"
      cmd="`type "$cmd" 2>/dev/null || { echo "error: command $cmd not found; exiting ..." 1>&2; exit 1; }`"
      [ $? != 0 ] && exit 1
      case "$cmd" in
        *\ /*) exit 0;;
            *) printf "%s\n" "error: $cmd" 1>&2; exit 1;;
      esac
   ' _ "$1" || exit 1
}

# Get your standard $PATH value
#PATH="$(command -p getconf PATH)"
typep ls
typep builtin
typep ls-temp

Bash 4.2.24 (2)를 사용 하는 Mac OS X v10.6.8 (Snow Leopard) command -v ls에서는 이동 된 것과 일치하지 않습니다 /bin/ls-temp.


0

프로그램이 존재하는 경우 경우 검사 할 프로그램을, 정말 아닙니다 강타 내장 명령 , 다음 command, typehash 수없는 명령에 내장 위해 그들이 모든 반환 0 종료 상태를 테스트하기에 적합합니다.

예를 들어 시간 내장 명령 보다 더 많은 기능을 제공 하는 시간 프로그램이 있습니다. 프로그램이 존재하는지 확인하려면 다음 예제와 같이 사용 하는 것이 좋습니다 .which

# First check if the time program exists
timeProg=`which time`
if [ "$timeProg" = "" ]
then
  echo "The time program does not exist on this system."
  exit 1
fi

# Invoke the time program
$timeProg --quiet -o result.txt -f "%S %U + p" du -sk ~
echo "Total CPU time: `dc -f result.txt` seconds"
rm result.txt

-1

스크립트

#!/bin/bash

# Commands found in the hash table are checked for existence before being
# executed and non-existence forces a normal PATH search.
shopt -s checkhash

function exists() {
 local mycomm=$1; shift || return 1

 hash $mycomm 2>/dev/null || \
 printf "\xe2\x9c\x98 [ABRT]: $mycomm: command does not exist\n"; return 1;
}
readonly -f exists

exists notacmd
exists bash
hash
bash -c 'printf "Fin.\n"'

결과

 [ABRT]: notacmd: command does not exist
hits    command
   0    /usr/bin/bash
Fin.

-1

매우 간단하기 때문에 이것을 사용합니다.

if [ `LANG=C type example 2>/dev/null|wc -l` = 1 ];then echo exists;else echo "not exists";fi

또는

if [ `LANG=C type example 2>/dev/null|wc -l` = 1 ];then
echo exists
else echo "not exists"
fi

쉘 내장 및 프로그램의 에코 상태를 표준 출력에 사용하고 표준 오류에는 아무것도 사용하지 않습니다. 반면에 명령을 찾지 못하면 상태를 표준 오류로만 에코합니다.


-1

이미 안전한 쉘 관행을 따르고 있다고 가정합니다 .

set -eu -o pipefail
shopt -s failglob

./dummy --version 2>&1 >/dev/null

이것은 버전을보고하거나 도움말을 표시하는 것과 같이 (거의) 아무것도하지 않는 방식으로 명령을 호출 할 수 있다고 가정합니다.

는 IF dummy명령이 발견되지 않는, 강타 다음과 같은 오류로 종료 ...

./my-script: line 8: dummy: command not found

command -v오류 메시지가 자동으로 생성되고 관련 줄 번호가 포함되어 있기 때문에 다른 (및 유사한) 답변 보다 더 유용하고 덜 장황합니다 .


이 답변은이 코드가 어떻게 사용되어야하는지 명시하지 않기 때문에 쉘에 좋지 않은 사람들에게는 매우 분명하지 않으며, dummy존재 에 대한 테스트의 실패한 지점 이 스크립트 작성자. 또한 작동 방식과 실행 환경의 설정을 변경한다는 설명이 부족합니다.
Palec

이것은 Bash 질문이지만 POSIX 이외의 shopt전화를 POSIX로 대체 할 수 있다는 점을 언급 할 가치가 있습니다 set -f. pipefail 옵션은 POSIX에서 지원되지 않습니다 하지만, 아무런 대안, AFAIK이 없습니다.
Palec

@Palec 나는 이것이 얼마나 어려운지 잘 모르겠습니다. OP에 요청한대로 명령을 찾을 수없고 오류와 함께 종료 될 수 있음을 사용자에게 알리기 위해 쉘에 의존하기 때문에 실제로 더 간단합니다. 내가 놓친 것을 알려주세요 :)
Adrien
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.