長いパスで最初に見つからないディレクトリを見つけるにはどうすればよいですか?


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

回答:


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の「パス名展開」を回避set -fしています

@BinaryZebra-何も避けていません。これを堅牢に行うために必要な環境を指定しています。それで全部です。no- デフォルト値に戻らず、askerのコンピューターに自身をインストールしたり、朝食を作ったりしません。
mikeserv

1
@BinaryZebra-by by、もし良いプログラマーがそうするように、実行環境に不必要に影響を与えたり回復したりすることを心配することが多いなら、nsここで使うとこの議論が無意味になります。
mikeserv

12

私のお気に入りのユーティリティの1つは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は、ヘルプやマニュアルページでバージョンを提供しておらず、バージョン情報を提供するフラグをサポートしていません。パッケージ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

引数がディレクトリ(またはディレクトリへのシンボリックリンク)であることを前提としています。
ステファンシャゼル16

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"

これは私のために働いた。パス文字列の最後の有効なパスが必要だったので、whileループの最初の行として(インクリメントされる前に)保存papa_prevました。「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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.