실행 중인 스크립트가 포함된 디렉터리의 경로를 안정적으로 식별합니다.

실행 중인 스크립트가 포함된 디렉터리의 경로를 안정적으로 식별합니다.

SO에는 이 문제를 해결하는 많은 답변이 있습니다.

좀 남았어유제유닉스와 리눅스에서원천스크립트.

하지만 그 중 일부는 Bash에만 적용됩니다(Bash에 의존함 $BASH_SOURCE).그들 중 일부디렉토리 이름에 개행 문자가 포함되어 있으면 작동하지 않으며 일부는 기호 링크가 포함되어 있으면 제대로 작동하지 않습니다.

무엇인가요믿을 수 있는모든 기본 셸에서 실행/호출 스크립트가 포함된 디렉터리의 경로를 얻는 방법은 무엇입니까? 각 쉘 유형을 확인하는 솔루션이 있으면 좋을 것입니다.

답변1

나는 이것을 처음 시도해 볼 것이다. 다른 사람들도 개선되기를 바랍니다.

스크립트를 실행하기 전에 쉘은 파일에 대한 파일 설명자를 엽니다. 일반적으로 fd 255에 할당됩니다. 어쨌든 open 이 있으면 찾을 수 있습니다 fd. lsof그래서 우리는 lsof -p $$가장 높은 파일 설명자의 파일 이름을 사용하고 얻습니다. lsof모든 UNIX 버전에 적용되는 것은 아닙니다. BSD 위키에는 동등한 것이 있다고 나와 있습니다 fstat. Darwin(Mac OS)에 있는 것 같습니다. `-F로

예시 스크립트:

#!/bin/sh

this_script_path=`lsof -p $$  | awk '/\/'${0##*/}'$/' | cut -c 55-`

분명히 클리핑은 lsof의 특정 형식에 크게 의존합니다. 버전 2에서는 이 문제를 완화할 수 있습니다. 참고: 내 버전에서는 lsof인쇄할 수 없는 문자를 변환하여 경로 이름의 탭 문자도 \t.

버전 2. 못생긴 Perl 코드에 대해 미리 죄송합니다. 이번에는 -F옵션을 사용하여 출력을 제어하겠습니다. 우리는 -F fn다음과 같은 결과를 얻게 될 것입니다:

p3834
fcwd
n/home/joe/test
frtd
n/
ftxt
n/bin/bash
fmem
n/lib64/ld-2.12.so
fmem
n/lib64/libdl-2.12.so
fmem
n/lib64/libc-2.12.so
fmem
n/lib64/libtinfo.so.5.7
fmem
n/usr/lib/locale/locale-archive
fmem
n/usr/lib64/gconv/gconv-modules.cache
f0
n/dev/pts/1
f1
n/dev/pts/1
f2
n/dev/pts/1
f255
n/home/joe/test/t.sh

우리는 가장 높은 파일 설명자(255라고 믿을 수 없다고 가정합니다)가 스크립트 이름이 되도록 이 혼란스러운 부분을 변환해야 합니다. (이것도 dash적용되는 것 같습니다.)

this_script_path=`lsof -p $$ -F fn | 
  perl -lane '
        $fd=$1,next if /^f(\d+)/; 
        $p{$fd}=$1 if $fd and /^n(.*)/;
        $fd="";
  }END { 
        @x=sort {$a<=>$b} keys %p;
        print $p{$x[-1]}; 
  }{'`

Perl 스크립트는 보기 흉하다는 점에 동의합니다. 이것은 한 줄짜리이며 명확성을 위해 분류했습니다. 줄이 로 시작하면 파일 설명자의 번호를 캡처합니다. f유효한 파일 설명자와 유효한 파일 이름이 있으면 파일 이름을 해시로 캡처합니다. 만일을 대비하여 이러한 조건 중 어느 것도 충족되지 않으면 을 취소합니다 $fd. 모든 행이 처리된 후 해시(파일 설명자)의 키를 숫자로 정렬하고 결과를 배열에 저장한 다음 배열의 마지막 요소(가장 큰 값)로 색인이 지정된 x파일 이름 hash의 내용을 출력합니다 .px

유일한 질문은 lsof가 모든 시스템에 설치되는지 여부와 이 출력 형식이 얼마나 안정적인지입니다.

답변2

도움이 될 수 있는 몇 가지 경험적 방법이 있지만 완전히 신뢰할 수 있는 방법은 없습니다.

Otheus는 파일 설명자를 사용하는 방법을 보여줍니다.. 이는 대부분의 경우에 작동하는 좋은 경험적 방법입니다. 그러나 실패하고 실패를 감지할 수 없는 극단적인 경우가 있습니다.

예: 다음 스크립트를 사용하세요.

#!/bin/sh
set
lsof -p$$ | sed 's/[0-9][0-9]*//'

이 스크립트의 복사본 두 개를 만듭니다. 하나는 이름이 지정 foo되고 다른 하나는 bar. 이제 조금 강조해 보겠습니다.

$ env -i PATH=/bin:/usr/bin perl -MPOSIX -e 'dup2(4, 11) or die $!; exec "dash", "foo", "bar"' 3<foo 4<bar </dev/null >foo.out
$ env -i PATH=/bin:/usr/bin perl -MPOSIX -e 'dup2(3, 10) or die $!; exec "dash", "bar", "foo"' 3<foo 4<bar </dev/null >bar.out
$ diff foo.out bar.out

17c17 < dash gils 1w reg 0,24 99 10130024 /tmp/202954/foo.out ---

대시 자일스 1w REG 0,24 99 10130022 /tmp/202954/bar.out

여기서 유일한 차이점은 출력을 기록하는 파일입니다.

이 경험적 방법이 실패하는 또 다른 경우는 쉘이 표준 입력에서 호출되거나 -c.

또 다른 접근 방식은 쉘의 명령줄을 구문 분석하는 것입니다. Linux에서는 /proc인수가 null로 구분되므로 휴대용 셸 도구로 구문 분석하기 어렵지만(최근 GNU 도구를 사용하면 더 쉬워짐) 수행할 수 있습니다. 이식 가능한 매개변수에 액세스하려면 호출해야 하며 ps명시적인 출력 형식은 없습니다. ps -o args매개변수는 공백으로 연결되며 잘릴 수 있습니다.

Linux에서도 여기서 발생하는 문제는 스크립트가 알지 못하는 옵션으로 셸이 호출될 수 있고 해당 옵션 중 하나가 인수를 취할 수 있다는 것입니다. 예를 들어, 이름이 붙은 스크립트 1와 이름이 다른 스크립트가 있다고 가정해 보겠습니다 2.

mksh -T 1 2

그러면 mksh가 호출되고 /dev/tty1스크립트가 실행됩니다 2.

zsh -T 1 2

옵션을 사용하여 zsh를 호출하고 cprecedences인수를 사용하여 스크립트를 실행합니다.12

각 쉘을 구별하려면 각 쉘을 알아야 합니다. 이 접근 방식을 사용하면 극단적인 경우를 감지할 수 있습니다. 비표준 옵션이 보이면 그냥 종료하세요.

관련 정보