Come posso trovare la prima directory mancante in un lungo percorso?


14

Immagina di avere un percorso che non esiste:

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

Ma diciamo /foo/bar che esiste. Esiste un modo rapido per determinare quale bazsia il punto di rottura nel percorso?

Sto usando Bash.


access(2)non è molto granulare, quindi la soluzione prevede in genere di scrivere qualcosa per iterare e testare ogni elemento del percorso a sua volta ...
thrig

Risposte:


5

Dato un percorso canonico, come il tuo, funzionerà:

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

Che stampa attraverso l'ultimo componente completamente esistente / accessibile di $pathnamee mette ciascuno di questi separatamente nell'array arg. Il primo componente inesistente non viene stampato, ma viene salvato in $p.

Potresti affrontarlo in modo opposto:

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

Che tornerà in modo appropriato o ridurrà $pathse necessario. Rifiuta di tentare una modifica /, ma in caso di successo stamperà sia la directory di lavoro corrente sia la directory in cui passa a stdout. $PWDVerrà inserita anche la tua corrente $OLDPWD.


Chiaramente: for p in $pathname sarà soggetto a "suddivisione delle parole" e "espansione del percorso".

1
@BinaryZebra - sì, è diviso $IFS. funziona esattamente così. non è soggetto all'espansione del nome percorso, tranne per il fatto che la variabile qui chiamata $pathnameviene espansa in una matrice di componenti percorso come suddivisa $IFS.
Mikeserv,

Stai evitando l'espansione "Pathname" su var $pathname usando set -f"che non viene riportato al suo valore predefinito.

@BinaryZebra - non sto evitando nulla. Sto specificando l'ambiente di cui ho bisogno per fare questo in modo robusto. È tutto. no - non è tornato al suo valore predefinito e non si installerà sul computer del richiedente né mi farà colazione.
Mikeserv,

1
@BinaryZebra - a proposito se, come dovrebbe fare un buon programmatore, ti trovi spesso preoccupato di influire / ripristinare inutilmente il tuo ambiente di esecuzione, potresti interessarti ns, che, se usato qui, renderebbe discutibile questa discussione.
Mikeserv,

12

Una delle mie utility preferite è namei, parte di util-linuxe quindi generalmente presente solo su Linux:

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

Ma il suo output non è molto analizzabile. Quindi, se desideri solo sottolineare che manca qualcosa, nameipotrebbe essere utile.

È utile per la risoluzione dei problemi generali di accesso a un percorso, poiché è possibile indurlo a indicare se un componente è un collegamento o un punto di montaggio, nonché le relative autorizzazioni:

$ 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

La capitale Dindica un punto di montaggio.


@ StéphaneChazelas Speravo che fossero presenti equivalenti su altri sistemi. Niente per i BSD?
muru,

nameifunziona per me fintanto che lo alimenta un percorso che esiste, ma quando lo do uno che non lo capisco namei: failed to stat: /usr/share/foo/bar: No such file or directory.
Kuzzooroo,

@kuzzooroo quale versione di namei?
Muru,

la mia versione di namei non fornisce una versione nel suo aiuto né nella sua pagina man e non supporta un flag che fornisce informazioni sulla versione. Sono in grado di determinare che proviene dal pacchetto linux-utils 2.16-1ubuntu5 ma non riesco a capire cosa implichi per la versione di namei.
Kuzzooroo,

@kuzzooroo Dato che anche Ubuntu 12.04 ha la 2.20.1 , devi essere davvero su una versione molto vecchia di Ubuntu. 10.04?
Muru,

1

Qualcosa del genere (tenendo conto dei nomi dei percorsi con spazi vuoti incorporati):

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

Presuppone che gli argomenti debbano essere directory (o collegamenti simbolici a directory).
Stéphane Chazelas,

1
se solo la cd not_a_directorytua shell scriverà a stderr qualcosa del genere cd:cd:6: no such file or directory: not_a_directory. In realtà, la shell dell'utente lo farà in un formato con cui probabilmente l'utente ha già molta familiarità. È quasi sempre più facile e migliore alla fine, comunque, fare semplicemente cose e lasciare che la shell gestisca i rapporti, se necessario. Questo tipo di filosofia richiede tuttavia un'attenzione molto rigorosa per restituire valori e la promozione / conservazione degli stessi.
Mikeserv,

1

Solo una soluzione alternativa per bash, supponendo che il percorso sia un percorso assoluto (inizia con /):

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

Questo ha funzionato per me. Avevo bisogno dell'ultimo percorso valido nella stringa del percorso, quindi l'ho salvato pacome pa_prevprima riga del ciclo while (prima che venga incrementato). Quando il test per "pa" fallisce, pa_prevha l'ultima directory esistente nel percorso indicato.
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

non è semplice ma puoi inserirlo in uno script, renderlo eseguibile e incollarlo da qualche parte nel tuo percorso, se lo utilizzerai frequentemente.

Avvertenza: se il nome della directory a qualsiasi livello contiene un spacecarattere, NON funzionerà.


Questo codice è bash? Ho dovuto scambiare in $ {dir} stava arrivando in bianco quindi ho scambiato in $ 1, ma ora sdir sta venendo in bianco per me.
kuzzooroo,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.