Wie finde ich das erste fehlende Verzeichnis in einem langen Pfad?

14

Stellen Sie sich vor, ich habe einen Pfad, den es nicht gibt:

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

Aber sagen wir, /foo/bar es gibt sie. Gibt es für mich eine schnelle Möglichkeit, festzustellen, ob dies bazder Bruchpunkt im Pfad ist?

Ich benutze Bash.

kuzzooroo
quelle
access(2)ist nicht sehr detailliert, daher besteht die Lösung in der Regel darin, etwas zu schreiben, um jedes
Pfadelement der

Antworten:

5

Bei einem kanonischen Pfadnamen wie dem Ihren funktioniert Folgendes:

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

Das druckt durch die letzte vollständig vorhandene / zugängliche Komponente von $pathnameund fügt jede dieser Komponenten separat in das arg-Array ein. Die erste nicht vorhandene Komponente wird nicht gedruckt, aber in gespeichert $p.

Sie könnten es umgekehrt angehen:

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

Das wird entweder angemessen zurückgegeben oder nach $pathBedarf reduziert . Es wird abgelehnt, eine Änderung zu versuchen. /Wenn dies jedoch erfolgreich ist, werden sowohl das aktuelle Arbeitsverzeichnis als auch das Verzeichnis gedruckt, in das stdout geändert wird. Ihr Strom $PWDwird ebenfalls eingespeist $OLDPWD.

mikeserv
quelle
Klar: for p in $pathnameunterliegt "Wortteilung" und "Pfadnamenerweiterung".
1
@ BinaryZebra - ja, es ist aufgeteilt auf $IFS. Genau so funktioniert es. Es unterliegt keiner Pfadnamenerweiterung, mit der Ausnahme, dass die hier aufgerufene Variable $pathnamezu einem Array von Pfadkomponenten erweitert wird, die aufgeteilt werden $IFS.
mikeserv
Sie vermeiden die "Pfadnamenerweiterung" für var $pathname mit set -f", die nicht auf den Standardwert zurückgesetzt wird.
@ BinaryZebra - ich vermeide nichts. Ich spezifiziere die Umgebung, die ich brauche, um dies robust zu tun. das ist alles. nein - es wird nicht auf den Standardwert zurückgesetzt und es wird sich auch nicht auf dem Computer des Fragestellers installieren oder mir Frühstück machen.
mikeserv
1
@BinaryZebra - Wenn Sie, wie es ein guter Programmierer tun sollte, häufig befürchten, Ihre Ausführungsumgebung unnötig zu beeinträchtigen oder wiederherzustellen, könnte dies für Sie von Interesse sein ns.
mikeserv
12

Eine meiner Lieblingsanwendungen ist ein nameiTeil von util-linuxund daher generell nur unter Linux:

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

Die Ausgabe ist jedoch nicht sehr syntaktisch. Wenn Sie also nur darauf hinweisen möchten, dass etwas fehlt, ist dies nameimöglicherweise hilfreich.

Dies ist nützlich, um allgemeine Probleme beim Zugriff auf einen Pfad zu beheben, da Sie feststellen können, ob es sich bei einer Komponente um einen Link oder einen Mount-Punkt handelt, und um die Berechtigungen:

$ 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

Die Hauptstadt Dgibt einen Einhängepunkt an.

muru
quelle
@ StéphaneChazelas Ich hoffte, dass Äquivalente auf anderen Systemen vorhanden sein könnten. Nichts für die BSDs?
Muru
nameifunktioniert für mich so lange es einen Pfad gibt, aber wenn ich ihm einen gebe, den ich nicht bekomme namei: failed to stat: /usr/share/foo/bar: No such file or directory.
Kuzzooroo
@ Kuzzooroo Welche Version von Namei?
muru
Meine Version von namei gibt weder eine Version in der Hilfe noch in der Manpage an und unterstützt kein Flag mit Versionsinformationen. Ich kann feststellen, dass es aus dem Paket linux-utils 2.16-1ubuntu5 stammt, kann aber nicht herausfinden, was dies für die Version von namei bedeutet.
Kuzzooroo
@kuzzooroo Da auch Ubuntu 12.04 2.20.1 hat , müssen Sie in der Tat auf einer sehr alten Version von Ubuntu sein. 10.04?
muru
1

So ähnlich (Pfadnamen mit eingebetteten Leerzeichen berücksichtigen):

#!/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
Thomas Dickey
quelle
Es wird davon ausgegangen, dass die Argumente Verzeichnisse (oder Symlinks zu Verzeichnissen) sind.
Stéphane Chazelas
1
wenn du nur cd not_a_directorydeine Shell schreibst, schreibst du einfach so etwas wie stderr cd:cd:6: no such file or directory: not_a_directory. Tatsächlich wird die Shell des Benutzers dies in einem Format tun, mit dem der Benutzer wahrscheinlich bereits sehr vertraut ist. Es ist ohnehin fast immer einfacher und besser, nur Dinge zu erledigen und die Shell die Berichterstellung nach Bedarf erledigen zu lassen. Diese Art von Philosophie erfordert jedoch eine sehr strenge Beachtung der Rückgabewerte und der Förderung / Bewahrung derselben.
mikeserv
1

Nur eine alternative Lösung für bash, vorausgesetzt, der Pfad ist ein absoluter Pfad (beginnt mit /):

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

quelle
Das hat bei mir funktioniert. Ich brauchte den letzten gültigen Pfad in der Pfadzeichenfolge, also habe ich ihn paals pa_preverste Zeile der while-Schleife gespeichert (bevor er inkrementiert wird). Wenn der Test für "pa" dann fehlschlägt, pa_prevhat das zuletzt vorhandene Verzeichnis den angegebenen Pfad.
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

Es ist nicht einfach, aber Sie können es in ein Skript einfügen, es ausführbar machen und es irgendwo in Ihrem Pfad ablegen, wenn Sie es häufig verwenden.

Vorsichtsmaßnahme: Wenn Ihr Verzeichnisname auf einer beliebigen Ebene ein spaceZeichen enthält , funktioniert dies NICHT.

MelBurslan
quelle
Ist das Bash-Code? Ich musste eintauschen. $ {Dir} war leer, also habe ich $ 1 eingetauscht, aber jetzt ist sdir für mich leer.
Kuzzooroo