Jak znaleźć pierwszy brakujący katalog na długiej ścieżce?


14

Wyobraź sobie, że mam ścieżkę, która nie istnieje:

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

Ale powiedzmy /foo/bar nie istnieje. Czy mogę szybko ustalić, czy bazjest to punkt krytyczny na ścieżce?

Używam Bash.


access(2)nie jest zbyt szczegółowe, więc rozwiązanie zazwyczaj polega na napisaniu czegoś do iteracji i przetestowaniu kolejno każdego elementu ścieżki ...
thrig

Odpowiedzi:


5

Biorąc pod uwagę kanoniczną nazwę ścieżki, taką jak Twoja, zadziała to:

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

Spowoduje to wydrukowanie ostatniego w pełni istniejącego / dostępnego komponentu $pathnamei umieszczenie każdego z nich osobno w tablicy arg. Pierwszy nieistniejący składnik nie jest drukowany, ale jest zapisywany $p.

Możesz podejść do tego przeciwnie:

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

To albo wróci odpowiednio, albo w $pathrazie potrzeby zmniejszy koszty . Odmawia próby zmiany /, ale jeśli się powiedzie, wydrukuje zarówno bieżący katalog roboczy, jak i katalog, w którym zmienia się na standardowe wyjście. Twój prąd również $PWDzostanie włączony $OLDPWD.


Oczywiście: for p in $pathnamebędą podlegały „dzieleniu słów” i „rozwijaniu nazw ścieżek”.

1
@BinaryZebra - tak, jest podzielony na $IFS. dokładnie tak to działa. nie podlega rozszerzaniu nazw ścieżek, z tym wyjątkiem, że zmienna tutaj nazywana $pathnamejest rozwijana do tablicy składników ścieżek w podziale $IFS.
mikeserv

$pathnameset -f

@BinaryZebra - niczego nie unikam. Im określam środowisko, którego potrzebuję, aby zrobić to solidnie. to wszystko. nie - nie powrócił do wartości domyślnej i nie zainstaluje się na komputerze pytającego ani nie zrobi mi śniadania.
mikeserv

1
@BinaryZebra - jeśli tak, jak powinien każdy dobry programista, często martwisz się, że niepotrzebnie wpłyniesz na / odzyskasz swoje środowisko wykonawcze, możesz zainteresować się tym ns, co, jeśli zostanie użyte tutaj, sprawi, że dyskusja stanie się dyskusyjna.
mikeserv

12

Jednym z moich ulubionych programów jest nameiczęść util-linuxi stąd ogólnie obecna tylko w Linuksie:

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

Ale jego wyniki nie są bardzo analizowalne. Więc jeśli chcesz tylko wskazać, że czegoś brakuje, nameimoże być przydatne.

Przydaje się to do rozwiązywania ogólnych problemów z dostępem do ścieżki, ponieważ można ustawić, czy komponent jest linkiem czy punktem podłączenia, a także uprawnienia:

$ 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

Stolica Dwskazuje punkt montowania.


@ StéphaneChazelas Miałem nadzieję, że odpowiedniki mogą być obecne w innych systemach. Nic dla BSD?
muru

nameidziała dla mnie tak długo, jak jest to kanał, który istnieje, ale gdy dam mu taki, którego nie dostanę namei: failed to stat: /usr/share/foo/bar: No such file or directory.
kuzzooroo

@kuzzooroo która wersja namei?
muru

moja wersja namei nie podaje wersji w swojej pomocy ani na stronie man i nie obsługuje flagi podającej informacje o wersji. Jestem w stanie stwierdzić, że pochodzi on z pakietu linux-utils 2.16-1ubuntu5, ale nie mogę zrozumieć, co to oznacza dla wersji namei.
kuzzooroo,

@kuzzooroo Ponieważ nawet Ubuntu 12.04 ma wersję 2.20.1 , musisz być na bardzo starej wersji Ubuntu. 10.04?
muru

1

Coś w tym stylu (uwzględnianie ścieżek z osadzonymi odstępami):

#!/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

Zakłada, że ​​argumentami są katalogi (lub dowiązania symboliczne do katalogów).
Stéphane Chazelas

1
jeśli tylko cd not_a_directorytwoja powłoka napisze do stderr coś w rodzaju cd:cd:6: no such file or directory: not_a_directory. W rzeczywistości powłoka użytkownika zrobi to w formacie, z którym użytkownik prawdopodobnie już jest bardzo dobrze zaznajomiony. W końcu prawie zawsze jest to łatwiejsze i lepsze, aby po prostu robić różne rzeczy i pozwolić powłoce obsługiwać raportowanie w razie potrzeby. Tego rodzaju filozofia wymaga jednak bardzo ścisłej uwagi na temat zwracanych wartości oraz ich promowania / zachowania.
mikeserv

1

Tylko alternatywne rozwiązanie dla bash, zakładając, że ścieżka jest ścieżką absolutną (zaczyna się od /):

#!/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"

To zadziałało dla mnie. Potrzebowałem ostatniej poprawnej ścieżki w ciągu ścieżki, więc zapisałem ją pajako pa_prevpierwszą linię pętli while (zanim zostanie zwiększona). Gdy test „pa” nie powiedzie się, pa_prevma ostatni istniejący katalog w podanej ścieżce.
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

nie jest to proste, ale możesz umieścić go w skrypcie, sprawić, by był wykonywalny i umieścić go gdzieś na swojej ścieżce, jeśli będziesz go często używać.

Zastrzeżenie emptor: Jeśli nazwa katalogu na dowolnym poziomie zawiera spaceznak, to NIE zadziała.


Czy to jest kod bashowy? Musiałem zamienić $ {dir} nadchodziło puste, więc zamieniłem 1 $, ale teraz sdir jest dla mnie pusty.
kuzzooroo
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.