web-dev-qa-db-ja.com

パスのリストがある場合、前述のパスのサブディレクトリを除外するにはどうすればよいですか?

私の答えのような絶対パスのソートされたリストがあるとしましょう here (この質問のために短縮され、変更されました):

/proc
/proc/sys/fs/binfmt_misc
/proc/sys/fs/binfmt_misc
/run
/run/cgmanager/fs
/run/hugepages/kvm
/run/lock
/run/user/1000
/run/user/1000/gvfs
/tmp
/home/bytecommander/ramdisk

私が望むのは、前述のパスのサブディレクトリであるすべてのパスを削除することにより、このリストを減らすことです。つまり、指定された入力に対して、次の出力が必要です。

/proc
/run
/tmp
/home/bytecommander/ramdisk

どのようにこれをコマンドラインで簡単に行うことができますか? Bash、sedawkまたはその他の一般的なツール1行に収まる短いソリューションは歓迎されますが、必須ではありません。

7
Byte Commander

AWK

$ awk -F '/' 'oldstr && NR>1{ if($0!~oldstr"/"){print $0;oldstr=$0}};NR == 1{print $0;oldstr=$0}'  paths.txt 
/proc
/run
/tmp
/home/bytecommander/ramdisk
/var/zomg
/var/zomgkthx
/zomg
/zomgkthx

これが機能する方法は簡単ですが、コマンドの順序は重要です。最初の行が何であるかを記録し、それを印刷することから始めます。次の行に移動して、次の行に前のテキストが含まれているかどうかを確認します。もしそうなら-何もしません。そうでない場合-それは別の新しいパスです。

元のアプローチは欠陥があり、/var/zomg/var/zomgkthxなどの同じ先行部分文字列を持つ隣接パスがあると失敗しました(指摘してくれたChai T.Rexに感謝します)。トリックは、「/」を古いパスに追加して終了を意味し、サブストリングを壊すことです。以下のpython代替案でも同じアプローチが使用されます。

Pythonの代替

#!/usr/bin/env python
import sys,os

oldline = None
with open(sys.argv[1]) as f:
     for index,line in enumerate(f):
         path = line.strip()
         if index == 0 or not line.startswith(oldline):
             print(path)
             oldline = os.path.join(path,'')

サンプル実行:

$ ./reduce_paths.py paths.txt                                                                                     
/proc
/run
/tmp
/home/bytecommander/ramdisk
/var/zomg
/var/zomgkthx
/zomg
/zomgkthx

このアプローチはawk-oneに似ています。考え方は同じです。最初の行を記録し、開始部分文字列として追跡変数を持たない行に遭遇した場合にのみ、追跡変数の印刷とリセットを続けます。

または、一度os.path.commonprefix()関数を使用することもできます。

#!/usr/bin/env python
import sys,os

oldline = None
with open(sys.argv[1]) as f:
     for index,line in enumerate(f):
         path = line.strip()
         if index == 0 or os.path.commonprefix([path,oldline]) != oldline:
             print(path)
             oldline = os.path.join(path,'')
10

新しいpathlibライブラリを使用する別のPythonバージョン:

#! /usr/bin/env python3

import pathlib, sys

seen = set()
for l in sys.stdin:
    p = pathlib.Path(l.strip())
    if not any(x in seen for x in p.parents):
        seen.add(p)
        print(str(p))
8
muru