如何找到长路径中第一个丢失的目录?


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)不是很精细,因此解决方案通常涉及编写一些东西来依次迭代和测试每个路径元素……
thrig

Answers:


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 '16

您在避免对VAR的“路径扩展” $pathname 通过使用set -f“未恢复到默认值。

@BinaryZebra-我没有避免任何事情。我指定了我需要的环境才能稳健地做到这一点。就这样。否-不会恢复为默认值,也不会安装在问问者的计算机上或让我吃早餐。
mikeserv '16

1
@BinaryZebra-顺便说一句,如果像任何优秀的程序员一样,您经常发现自己担心不必要地影响/恢复您的执行环境,您可能会对感兴趣ns,如果在这里使用它,将使讨论变得毫无意义。
mikeserv '16

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不需要什么?
muru

namei只要给它提供了一条存在的路径,它就可以为我工作,但是当我给它提供一条不存在的路径时,它就可以工作namei: failed to stat: /usr/share/foo/bar: No such file or directory
kuzzooroo's

@kuzzooroo namei的哪个版本?
muru

我的namei版本在其帮助或手册页中均未提供版本,并且不支持提供版本信息的标记。我能够确定它来自软件包linux-utils 2.16-1ubuntu5,但无法弄清楚那对namei版本的含义。
kuzzooroo's

@kuzzooroo因为甚至Ubuntu 12.04都具有2.20.1,所以您确实必须使用非常旧的Ubuntu版本。10.04?
muru

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

它假定参数是目录(或目录的符号链接)。
斯特凡Chazelas

1
如果你只是cd not_a_directoryshell,只会向stderr写像这样的东西cd:cd:6: no such file or directory: not_a_directory。实际上,用户的外壳将以用户可能已经非常熟悉的格式进行操作。无论如何,最终它几乎总是更容易和更好,只是一些事情并让Shell根据需要处理报告。但是,这种哲学确实需要非常严格地关注返回值及其提升/保存。
mikeserv '16

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"

这对我有用。我需要路径字符串中的最后一个有效路径,因此我另存papa_prevwhile循环的第一行(在它递增之前)。如果对“ 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

这并不简单,但是如果您打算经常使用它,则可以将其放在脚本中,使其可执行并粘贴到您的路径中。

注意:如果您在任何级别的目录名称中都包含space字符,则此操作将无效。


这是bash代码吗?我不得不将$ {dir}换成空白,所以我换了$ 1,但是现在sdir对我来说变成空白。
kuzzooroo's
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.