Bagaimana saya bisa menemukan direktori pertama yang hilang di jalur panjang?


14

Bayangkan saya memiliki jalan yang tidak ada:

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

Tapi katakanlah /foo/bar memang ada. Apakah ada cara cepat bagi saya untuk menentukan itu bazadalah titik putusnya di jalan?

Saya menggunakan Bash.


access(2)tidak terlalu granular, jadi solusinya biasanya melibatkan menulis sesuatu untuk beralih dan menguji setiap elemen path pada gilirannya ...
thrig

Jawaban:


5

Diberi nama path kanonik, seperti milik Anda, ini akan berfungsi:

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

Itu mencetak melalui komponen terakhir yang sepenuhnya tersedia / diakses $pathname, dan menempatkan masing-masing secara terpisah ke dalam array arg. Komponen pertama yang tidak ada tidak dicetak, tetapi disimpan di $p.

Anda mungkin mendekatinya secara berlawanan:

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

Itu akan kembali dengan tepat atau akan pare $pathsesuai kebutuhan. Itu menolak untuk mencoba perubahan /, tetapi jika berhasil akan mencetak direktori kerja Anda saat ini dan direktori yang diubahnya menjadi stdout. Saat ini Anda $PWDakan dimasukkan ke dalam $OLDPWDjuga.


Jelas: for p in $pathnameakan dikenakan "pemisahan kata" dan "Perluasan jalur".

1
@ Binzebra - ya, itu terpecah $IFS. itulah cara kerjanya. itu tidak tunduk pada ekspansi pathname, kecuali bahwa variabel di sini dipanggil $pathnamediperluas ke array komponen path sebagai split on $IFS.
mikeserv

Anda menghindari "Perluasan jalur" pada var $pathname dengan menggunakan set -f"yang tidak dikembalikan ke nilai default.

@ Binzebra - saya tidak menghindari apa pun. im menentukan lingkungan yang saya butuhkan untuk melakukan ini dengan kuat. itu saja. tidak - itu tidak dikembalikan ke nilai default dan tidak akan menginstal sendiri di komputer penanya atau membuat saya sarapan.
mikeserv

1
@BinaryZebra - oleh, jika, seperti halnya programmer yang baik, Anda sering merasa khawatir tentang tidak perlu mempengaruhi / memulihkan lingkungan eksekusi Anda, Anda mungkin tertarik ns, yang, jika digunakan di sini, akan membuat diskusi ini diperdebatkan.
mikeserv

12

Salah satu utilies favorit saya adalah namei, bagian dari util-linuxdan karenanya umumnya hanya ada di Linux:

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

Tetapi hasilnya tidak terlalu dapat diuraikan. Jadi, jika Anda hanya ingin menunjukkan ada sesuatu yang hilang, nameimungkin berguna.

Ini berguna untuk memecahkan masalah umum dalam mengakses jalur, karena Anda bisa membuatnya untuk menyatakan apakah suatu komponen adalah tautan atau titik pemasangan, serta izinnya:

$ 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

Ibu kota Dmenunjukkan titik pemasangan.


@ StéphaneChazelas Saya berharap padanan mungkin ada pada sistem lain. Tidak ada untuk BSD?
muru

nameibekerja untuk saya selama memberi makan jalan yang ada, tetapi ketika saya memberikannya yang tidak saya dapatkan namei: failed to stat: /usr/share/foo/bar: No such file or directory.
kuzzooroo

@kuzzooroo versi namei yang mana?
muru

versi namei saya tidak memberikan versi dalam bantuannya maupun halaman manualnya dan tidak mendukung flag yang memberikan info versi. Saya dapat menentukan bahwa itu berasal dari paket linux-utils 2.16-1ubuntu5 tetapi tidak dapat menemukan apa yang menyiratkan untuk versi namei.
kuzzooroo

@kuzzooroo Karena bahkan Ubuntu 12.04 memiliki 2.20.1 , Anda harus menggunakan versi Ubuntu yang sangat lama. 10,04?
muru

1

Sesuatu seperti ini (akuntansi untuk nama path dengan embedded kosong):

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

Ini mengasumsikan argumen dimaksudkan sebagai direktori (atau symlink ke direktori).
Stéphane Chazelas

1
jika Anda hanya cd not_a_directoryshell Anda akan menulis ke stderr sesuatu seperti cd:cd:6: no such file or directory: not_a_directory. Sebenarnya, shell pengguna akan melakukannya dalam format yang dengannya pengguna mungkin sudah sangat akrab. Ini hampir selalu lebih mudah dan lebih baik pada akhirnya untuk melakukan hal - hal dan membiarkan shell menangani pelaporan sebagaimana diperlukan. Filosofi semacam itu memang membutuhkan perhatian yang sangat ketat untuk mengembalikan nilai dan promosi / pelestarian yang sama.
mikeserv

1

Hanya solusi alternatif untuk bash, dengan asumsi path adalah path absolut (dimulai dengan /):

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

Ini berhasil untuk saya. Saya membutuhkan jalur valid terakhir dalam string jalur, jadi saya menyimpannya pasebagai pa_prevbaris pertama dari while loop (sebelum bertambah). Ketika tes untuk "pa" kemudian gagal, pa_prevmemiliki direktori terakhir yang ada di jalur yang diberikan.
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

itu tidak sederhana tetapi Anda dapat meletakkannya di skrip, membuatnya dapat dieksekusi dan menempelkannya di suatu tempat di jalur Anda, jika Anda akan sering menggunakannya.

Caveat emptor: Jika nama direktori Anda di tingkat mana pun mengandung spacekarakter, ini TIDAK akan berfungsi.


Apakah ini kode bash? Saya harus menukar $ {dir} sudah kosong jadi saya menukar $ 1, tapi sekarang sdir kosong untuk saya.
kuzzooroo
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.