긴 경로에서 첫 번째 누락 된 디렉토리를 어떻게 찾을 수 있습니까?


14

존재하지 않는 경로가 있다고 상상해보십시오.

$ ls /foo/bar/baz/hello/world
ls: cannot access /foo/bar/baz/hello/world: No such file or directory

그러나하자 말은 /foo/bar 하지 존재합니다. 그것이 baz경로의 중단 점 인지 판단하는 빠른 방법이 있습니까?

Bash를 사용하고 있습니다.


access(2)매우 세분화되지 않았으므로 솔루션에는 일반적으로 각 경로 요소를 반복적으로 테스트하고 테스트 할 무언가를 작성하는 것이 포함됩니다.
thrig

답변:


5

귀하와 같은 정식 경로 이름이 주어지면 다음과 같이 작동합니다.

set -f --; IFS=/
for p in $pathname
do    [ -e "$*/$p" ] || break
      set -- "$@" "$p"
done; printf %s\\n "$*"

이것은 완전히 존재하거나 접근 가능한 마지막 구성 요소를 통해 인쇄 $pathname하고 각 구성 요소를 개별적으로 arg 배열에 넣습니다. 존재하지 않는 첫 번째 구성 요소는 인쇄되지 않지만에 저장됩니다 $p.

반대로 접근 할 수 있습니다.

until cd -- "$path" && cd -
do    case   $path  in
      (*[!/]/*)
              path="${path%/*}"
;;    (*)   ! break
      esac
done  2>/dev/null   && cd -

적절하게 반환되거나 $path필요에 따라 줄어 듭니다 . 에 대한 변경 시도는 거부 /하지만 성공하면 현재 작업 디렉토리와 stdout으로 변경되는 디렉토리가 모두 인쇄됩니다. 현재 $PWD도 입력됩니다 $OLDPWD.


분명히 : for p in $pathname"단어 분할"및 "경로 확장"의 대상이됩니다.

1
@BinaryZebra-네, 분할되었습니다 $IFS. 그것이 정확히 작동하는 방식입니다. 여기에서 호출 $pathname된 변수가에서 분할 된 경로 구성 요소의 배열로 확장 된다는 점을 제외하고는 경로 이름 확장이 적용되지 않습니다 $IFS.
mikeserv

기본값을 반환하지 않는 "를 $pathname 사용하여 var에서"경로 확장 "을 피합니다 set -f.

@BinaryZebra-아무것도 피하지 않습니다. 나는 환경을 지정하고 이것을 강력하게하기 위해 필요합니다. 그게 다야. no- 기본값으로 돌아 오지 않으며 asker의 컴퓨터에 설치되거나 아침 식사를하지 않습니다.
mikeserv

1
@BinaryZebra-by by by, 만약 좋은 프로그래머라면, 실행 환경에 불필요하게 영향을 미치거나 복구하는 것에 대해 걱정하는 경우가 종종 있습니다 ns.
mikeserv

12

내가 가장 좋아하는 유틸리티 중 하나는 namei, 부분 util-linux이며 따라서 일반적으로 Linux에만 있습니다.

$ namei /usr/share/foo/bar
f: /usr/share/foo/bar
 d /
 d usr
 d share
   foo - No such file or directory

그러나 결과는 매우 구문 분석 할 수 없습니다. 따라서 누락 된 항목을 지적하려는 경우 namei유용 할 수 있습니다.

경로에 액세스 할 때 일반적인 문제를 해결하는 데 유용합니다. 구성 요소가 링크인지 또는 마운트 포인트인지 여부와 권한을 나타내는 경로를 얻을 수 있기 때문입니다.

$ ln -sf /usr/foo/bar /tmp/
$ namei -lx /tmp/bar
f: /tmp/bar
Drwxr-xr-x root    root    /
Drwxrwxrwt root    root    tmp
lrwxrwxrwx muru    muru    bar -> /usr/foo/bar
Drwxr-xr-x root    root      /
drwxr-xr-x root    root      usr
                             foo - No such file or directory

대문자 D는 마운트 지점을 나타냅니다.


@ StéphaneChazelas 다른 시스템에도 동등한 기능이있을 것으로 기대했습니다. BSD에는 아무것도 없습니까?
muru

namei피드에 존재하는 경로를 제공하는 한 나를 위해 작동하지만 얻을 수없는 경로를 제공하면 namei: failed to stat: /usr/share/foo/bar: No such file or directory.
kuzzooroo

@kuzzooroo 어떤 버전의 namei?
muru

내 namei 버전은 해당 도움말이나 해당 매뉴얼 페이지에서 버전을 제공하지 않으며 버전 정보를 제공하는 플래그를 지원하지 않습니다. 패키지 linux-utils 2.16-1ubuntu5에서 나온 것이지만 namei 버전에 어떤 의미가 있는지 알 수 없습니다.
kuzzooroo

@kuzzooroo 우분투 12.04 조차 2.20.1이기 때문에 , 당신은 실제로 아주 오래된 우분투 버전이어야합니다. 10.04?
muru

1

이와 같은 것 (빈칸이있는 경로 이름을 고려한) :

#!/bin/sh
explain() {
    if [ -d "$1" ]
    then
        printf "\t%s: is a directory\n" "$1"
    elif [ -e "$1" ]
    then
        printf "\t%s: is not a directory\n" "$1"
    else
        printf "\t%s: does not exist\n" "$1"
    fi
}

for item in "$@"
do
    last=
    test="$item"
    printf "testing: '%s'\n" "$item"
    while [ ! -d "$test" ]
    do
        last="$test"
        test=$(dirname "$test")
        [ -z "$test" ] && break
    done
    if [ -n "$last" ]
    then
        explain "$test"
        explain "$last"
    else
        printf "\t%s: ok\n" "$item"
    fi
done

인수가 디렉토리 (또는 디렉토리에 대한 심볼릭 링크) 인 것으로 가정합니다.
Stéphane Chazelas

1
당신 cd not_a_directory의 쉘이 stderr에 다음과 같은 것을 쓸 것 cd:cd:6: no such file or directory: not_a_directory입니다. 실제로, 사용자의 쉘은 사용자가 이미 매우 친숙한 형식으로 수행합니다. 어쨌든 결국에는 작업을 수행 하고 셸이 필요에 따라보고를 처리하도록 하기 위해 거의 항상 더 쉽고 더 좋습니다 . 그러나 이러한 철학은 가치를 반환하고이를 촉진 / 보전하기 위해 매우 엄격한주의를 기울여야합니다.
mikeserv

1

경로가 절대 경로 (/로 시작)라고 가정하면 bash에 대한 대체 솔루션입니다.

#!/bin/bash

pathname="$1"

IFS='/' read -r -a p <<<"${pathname#/}"

pa=""    max="${#p[@]}"    i=0
while (( i<"$max" )); do
      pa="$pa/${p[i++]}"
      if     [[ ! -e $pa ]]; then
             printf 'failed at: \t"%s"\t"%s"\n' "${pa##*/}" "${pa}"
             break
      fi
done

$ ./script "/foo/ba r/baz/hello/world"
failed at:      "hello"        "/foo/ba r/baz/hello"

이것은 나를 위해 일했습니다. 경로 문자열에 마지막 유효한 경로가 필요했기 때문에 while 루프의 첫 번째 줄로 저장 pa했습니다 pa_prev(증가되기 전에). "pa"에 대한 테스트가 실패 pa_prev하면 주어진 경로에 마지막 기존 디렉토리가 있습니다.
Ville

0
(( dirct=$(echo ${dir}|tr "/" " "|wc -w)+1 ))
i=2
while [ ${i} -le ${dirct} ]
do
  sdir=$(echo ${dir}|cut -d/ -f1,${i})
  if [ ! -d ${sdir} ]
  then
    echo "Path is broken at ${sdir}"
  fi
  (( i++ ))
done

간단하지는 않지만 스크립트를 자주 사용하려는 경우 스크립트에 넣고 실행 가능하게 만들고 경로 어딘가에 붙일 수 있습니다.

주의 사항 : 어떤 레벨의 디렉토리 이름에도 space문자 가 포함되어 있으면 작동하지 않습니다.


이 bash 코드입니까? $ {dir}에서 스왑해야 했으므로 $ 1로 스왑했지만 이제 sdir이 비어 있습니다.
kuzzooroo
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.