Як я можу знайти перший пропущений каталог на довгому шляху?


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)не дуже деталізований, тому рішення, як правило, передбачає написання чогось, щоб повторити і протестувати кожен елемент шляху по черзі ...
тричі

Відповіді:


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 $pathname , використовуючи set -f", який не повертається до значення за замовчуванням"

@BinaryZebra - я нічого не уникаю. я вказуючи на те, що мені потрібно для того, щоб зробити це надійно. це все. ні - його не повернуто до значення за замовчуванням, і він не встановить себе на комп'ютер запитувача і не зробить мені сніданок.
mikeserv

1
@BinaryZebra - тим самим, якщо, як і будь-який хороший програміст, ви часто переживаєте, що турбуєтесь про те, щоб непотрібно вплинути / відновити ваше середовище виконання, ви можете зацікавитись 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?
муру

nameiпрацює для мене до тих пір, як подавати це шлях, який існує, але коли я даю такий, який не отримую namei: failed to stat: /usr/share/foo/bar: No such file or directory.
kuzzooroo

@kuzzooroo, яка версія namei?
муру

моя версія namei не надає версії на допомогу, ані його man page та не підтримує прапор, який дає інформацію про версію. Я можу визначити, що він походить від пакета linux-utils 2.16-1ubuntu5, але не можу зрозуміти, що це означає для версії namei.
kuzzooroo

@kuzzooroo Оскільки навіть у Ubuntu 12.04 є 2.20.1 , ви дійсно повинні бути на дуже старій версії Ubuntu. 10,04?
муру

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

Він передбачає, що аргументи мають на увазі каталоги (або символьні посилання на каталоги).
Стефан Хазелас

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"

Це працювало для мене. Мені знадобився останній дійсний шлях у рядку шляху, тому я зберег paяк pa_prevперший рядок циклу while (до його збільшення). Коли тест на "pa" потім не вдається, pa_prevмає останній існуючий каталог у вказаному шляху.
Віль

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

це не просто, але ви можете помістити його в сценарій, зробити його виконуваним і вставити його кудись у свій шлях, якщо ви збираєтеся ним часто користуватися.

Caveat emptor: Якщо ім'я вашого каталогу на будь-якому рівні містить spaceсимвол, це НЕ буде працювати.


Це баш-код? Мені довелося поміняти обмін на $ {dir}, вийшов порожнім, тому я поміняв $ 1, але тепер sdir для мене виходить порожнім.
kuzzooroo
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.