Làm thế nào tôi có thể tìm thấy thư mục bị thiếu đầu tiên trong một con đường dài?


14

Hãy tưởng tượng tôi có một con đường không tồn tại:

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

Nhưng hãy nói rằng /foo/bar tồn tại. Có cách nào nhanh chóng để tôi xác định đó bazlà điểm phá vỡ trong đường dẫn không?

Tôi đang sử dụng Bash.


access(2)không phải là rất dạng hạt, vì vậy giải pháp thường liên quan đến viết một cái gì đó để lặp và kiểm tra từng yếu tố con đường lần lượt ...
thrig

Câu trả lời:


5

Đưa ra một tên đường dẫn chính tắc, chẳng hạn như của bạn, điều này sẽ hoạt động:

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

Điều đó in qua thành phần hoàn toàn hiện có / có thể truy cập cuối cùng của $pathnamevà đặt từng phần riêng biệt vào mảng arg. Thành phần không tồn tại đầu tiên không được in, nhưng nó được lưu trong $p.

Bạn có thể tiếp cận nó một cách trái ngược:

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

Điều đó sẽ trở lại một cách thích hợp hoặc sẽ giảm xuống $pathkhi cần thiết. Nó từ chối thử thay đổi /, nhưng nếu thành công sẽ in cả thư mục làm việc hiện tại của bạn và thư mục mà nó thay đổi thành thiết bị xuất chuẩn. Hiện tại của bạn $PWDsẽ được đưa vào $OLDPWDlà tốt.


Rõ ràng: for p in $pathnamesẽ phải chịu "chia tách từ" và "mở rộng tên đường dẫn".

1
@BinaryZebra - vâng, nó được chia ra $IFS. đó chính xác là cách nó hoạt động. nó không chịu sự mở rộng tên đường dẫn, ngoại trừ biến ở đây được gọi $pathnameđược mở rộng thành một mảng các thành phần đường dẫn như được phân chia trên $IFS.
mikeerv

Bạn đang tránh "mở rộng tên đường dẫn" trên var $pathname bằng cách sử dụng set -f"không được trả về giá trị mặc định của nó.

@BinaryZebra - tôi không tránh mặt gì cả. Tôi chỉ định môi trường tôi cần để làm điều này mạnh mẽ. đó là tất cả. không - nó không được trả về giá trị mặc định và nó cũng không tự cài đặt trên máy tính của người hỏi hoặc làm cho tôi bữa sáng.
mikeerv 4/2/2016

1
@BinaryZebra - bởi, nếu, như bất kỳ lập trình viên giỏi nào, bạn thường cảm thấy lo lắng về việc ảnh hưởng / phục hồi không cần thiết môi trường thực thi của mình, bạn có thể quan tâm ns, nếu được sử dụng ở đây, sẽ đưa ra cuộc thảo luận này.
mikeerv

12

Một trong những tiện ích yêu thích của tôi là namei, một phần util-linuxvà do đó thường chỉ hiện diện trên Linux:

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

Nhưng đầu ra của nó không phải là rất phân tích. Vì vậy, nếu bạn chỉ muốn chỉ ra điều gì đó còn thiếu, nameicó thể hữu ích.

Nó hữu ích để khắc phục các sự cố chung khi truy cập một đường dẫn, vì bạn có thể đưa nó ra để xác định xem một thành phần là một liên kết hoặc một điểm gắn kết, cũng như các quyền của nó:

$ 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

Vốn Dchỉ ra một điểm gắn kết.


@ StéphaneChazelas Tôi hy vọng tương đương có thể có mặt trên các hệ thống khác. Không có gì cho BSD?
muru

nameilàm việc cho tôi miễn là một nguồn cấp dữ liệu cho nó một con đường tồn tại, nhưng khi tôi cho nó một con đường mà tôi không nhận được namei: failed to stat: /usr/share/foo/bar: No such file or directory.
kuzzooroo

@kuzzooroo phiên bản nào của namei?
muru

phiên bản namei của tôi không cung cấp phiên bản trợ giúp cũng như trang man của nó và không hỗ trợ thông tin phiên bản cung cấp cờ. Tôi có thể xác định rằng nó đến từ gói linux-utils 2.16-1ubfox5 nhưng không thể tìm ra ý nghĩa của phiên bản namei.
kuzzooroo 7/2/2016

@kuzzooroo Vì ngay cả Ubuntu 12.04 cũng có 2.20.1 , bạn phải có phiên bản Ubuntu rất cũ. 10.04?
muru

1

Một cái gì đó như thế này (chiếm tên đường dẫn với khoảng trống được nhúng):

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

Nó giả sử các đối số có nghĩa là các thư mục (hoặc symlink đến các thư mục).
Stéphane Chazelas

1
nếu bạn chỉ cd not_a_directoryvỏ của bạn sẽ chỉ viết cho stderr một cái gì đó như cd:cd:6: no such file or directory: not_a_directory. Trên thực tế, trình bao của người dùng sẽ làm như vậy trong một định dạng mà người dùng có thể đã rất quen thuộc. Gần như luôn luôn dễ dàng hơn và tốt hơn cuối cùng chỉ là làm công cụ và để shell xử lý báo cáo khi cần thiết. Mặc dù loại triết lý đó đòi hỏi một sự chú ý rất nghiêm ngặt để trả về các giá trị và việc quảng bá / bảo tồn như nhau.
mikeerv

1

Chỉ là một giải pháp thay thế cho bash, giả sử đường dẫn là một đường dẫn tuyệt đối (bắt đầu bằng /):

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

Điều này làm việc cho tôi. Tôi cần đường dẫn hợp lệ cuối cùng trong chuỗi đường dẫn, vì vậy tôi đã lưu padưới pa_prevdạng dòng đầu tiên của vòng lặp while (trước khi nó được tăng lên). Khi kiểm tra "pa" không thành công, pa_prevcó thư mục hiện có cuối cùng trong đường dẫn đã cho.
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

nó không đơn giản nhưng bạn có thể đặt nó trong một tập lệnh, làm cho nó có thể thực thi được và dán nó ở đâu đó trên đường dẫn của bạn, nếu bạn sẽ sử dụng nó thường xuyên.

Caveat emptor: Nếu tên thư mục của bạn ở bất kỳ cấp nào có chứa một spaceký tự, điều này sẽ KHÔNG hoạt động.


Đây có phải là mã bash không? Tôi đã phải trao đổi trong $ {dir} sắp hết vì vậy tôi đã đổi ở $ 1, nhưng bây giờ sdir đang trống cho tôi.
kuzzooroo
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.