指定されたパスのすべての親ディレクトリパスを取得するには、何を使用できますか?

指定されたパスのすべての親ディレクトリパスを取得するには、何を使用できますか?

私は与えられたパスの親ディレクトリのリストを提供できるbashスクリプトで利用可能なツールを探しています。

foo/bar/moocowたとえば、出力したい入力があるとします。

foo
foo/bar
foo/bar/moocow

複数のパスを入力して独自の結果を得ることができれば良いでしょう。たとえば、次のようになります。

toolimlookingfor << EOF
dir1/file1
dir1/file2
foo/bar/moocow
EOF

出力:

dir1
dir1/file1
dir1/file2
foo
foo/bar
foo/bar/moocow

dirname私が探しているものに近いが、直系の親だけを提供します。私は自分自身とすべての親の道を探しています。

答え1

/tmpフォルダ内に新しいディレクトリを作成することに同意する場合は、findコマンドを使用できます。この方法では、一意のディレクトリ名のみを印刷することもできます。

#!/bin/bash
if [[ $# -eq 0 ]]; then exit; fi
mkdir -p /tmp/printnames
for i
do
  mkdir -p /tmp/printnames/"$i"
done
cd /tmp/printnames
find *  -type d -print
rm -r /tmp/printnames

したがって、一時ディレクトリ構造を作成し、検索を使用して移動できます。

答え2

標準Pythonスクリプトの使用pathlib入力の代わりにパラメータを処理するモジュール:

#! /usr/bin/env python3

import sys

from collections import OrderedDict
from pathlib import Path

paths = OrderedDict()  # to maintain ordering and skip duplicates

for arg in sys.argv[1:]:
    path = Path(arg)
    for subpath in reversed(path.parents):
        # add the parents
        paths[subpath.as_posix()] = subpath
    # add the path itself
    paths[path.as_posix()] = path

# we don't need '.' in the output
if '.' in paths:
    paths.pop('.')

print('\n'.join(paths.keys()))

(これはループを介して標準入力を使用するように簡単に変更できますsys.stdin。)

例:

% ./foo.py dir1/file1 dir1/file2 foo/bar/moocow
dir1
dir1/file1
dir1/file2
foo
foo/bar
foo/bar/moocow

答え3

このコードはこれを行います。一時ファイルを使用しません。スペースにはうまく機能しますが、改行文字はあいまいです(変更される\nように変更されます\0)。\0自動化されたプロセスで使用するには、それを使用する必要があります。\0Unix ファイル名に含まれないことが保証される唯一の文字です。

単に再帰的に使用されます。dirnames特定を削除/有効にすることでprintf調整できます。上下に移動したり、ルートノード(.または/)を表示しないようにします。あなたの例ではルートノードが表示されていませんが、必要だと思います。これがなければ/a/b/c出力はa/b/c

#!/bin/bash

function dirnames {
    local input
    local parent
    input="$1"
    if [[ "$input" == "" ]]
    then
        true
    elif [[ "$input" == "." ||  "$input" == "/" ]]
    then
        printf '%s\n' "$input" #print the root node
    else
        #printf '%s\n' "$input"  #print node (descending)
        parent="$(dirname "$input")"
        dirnames "$parent"
        printf '%s\n' "$input" #print node (ascending)
    fi
}

for d in "$@"
do
    dirnames "$d"
done

使用例

%↳ ./dirnames a/b/c
.
a
a/b
a/b/c
%↳ ./dirnames /a/b/c
/
/a
/a/b
/a/b/c
%↳

このように動作させるには

%↳ ./dirnames a/b/c
.
a
b
c
%↳ ./dirnames /a/b/c
/
a
b
c
%↳

次に、ファイルを別の名前で保存してdirnames実行しますsed -r -i '17 s/("[$]input")/"$(basename \1)"/' dirnames。これで、各レベルの葉だけを出力するスクリプトが生成されます。

答え4

私の場合のように、末尾のスラッシュ(または他の区切り文字)が効果がある場合は、次の解決策が最も簡単で透明です。

pth='one/two/three'
sep='/'

# split string into array by separator
arr=($(echo $pth | tr $sep '\n'))

# loop over array and append
sub=''
for index in ${!arr[@]}
do
    sub+="${arr[index]}$sep"
    printf "$index \t ${arr[index]} \t $sub \n"
done

出力:

0        one     one/
1        two     one/two/
2        three   one/two/three/

関連情報