2012-09-06 41 views
3

從存儲在文件f中的文件名列表中,找到dir下每個文件名的相對路徑,將這個新列表輸出到文件p的最佳方式是什麼?我目前使用以下內容:從文件名列表中查找路徑的有效方法

while read name 
do 
    find dir -type f -name "$name" >> p 
done < f 

這對於大型列表或大型目錄樹來說太慢了。

編輯:幾個數字:

  • dir下的目錄數:1870
  • 文件數量dir下:80622
  • f文件名數:73487

所有列在f中的文件確實存在於dir之下。

+0

因此,您的文件包含一個沒有路徑信息的文件名列表?你想如何處理多次出現的文件?我假設你要將整個目錄樹加載到內存中,以便快速完成此操作,因爲重複掃描磁盤永遠不會足夠快,但是否可行取決於我們所談論的大小。恐怕我不知道這樣做的方式,但如果這是一個選項,用PHP或類似的方法做這件事會很微不足道? – Basic

+0

是的,只是文件名,結果必須包含所有可能的路徑。可以將目錄樹加載到內存中,但我寧願不使用PHP,因爲它沒有安裝。儘管Perl或Python都可以。 – moatPylon

+2

+1使用'while read name ... done

回答

3

下面的一段python代碼可以做到這一點。關鍵是運行find一次並將輸出存儲在散列表中,以提供從file_name到文件名路徑列表的O(1)方法。

#!/usr/bin/env python 
import os 

file_names = open("f").readlines() 
file_paths = os.popen("find . -type f").readlines() 
file_names_to_paths = {} 
for file_path in file_paths: 
    file_name = os.popen("basename "+file_path).read() 
    if file_name not in file_names_to_paths: 
     file_names_to_paths[file_name] = [file_path] 
    else: 
     file_names_to_paths[file_name].append(file_path) # duplicate file 

out_file = open("p", "w") 
for file_name in file_names: 
    if file_names_to_paths.has_key(file_name): 
     for path in file_names_to_paths[file_name]: 
      out_file.write(path) 
+1

爲什麼不使用'os.path.walk'和'os.path.basename(file_path)'? – Neil

+0

必須使用os.path.basename中的特殊字符。當'file_path'包含空格 – moatPylon

+0

4秒時失敗,沒有丟失文件。如果沒有更短的內容出現,我會接受這個答案。 – moatPylon

1

我想這應該做的伎倆:

xargs locate -b < f | grep ^dir > p 

編輯:我想不出一個簡單的方法,以前綴dir/*/到文件名列表,否則你可以只通過直接到xargs locate

+0

'locate'使用參數作爲部分匹配,並打印絕對文件名。 'awk'{print「\\」$ 0}'f | xargs -d'\ n'找到-b | sed「s | $(pwd)/ || g」| grep^dir> d'可能會訣竅。 – moatPylon

+0

啊,是的,這些部分匹配,我的前綴想法沒有這個問題,但它當然有沒有找到'dir/$ file'的問題。 – Neil

2

嘗試此perl的單行

perl -e '%H=map{chomp;$_=>1}<>;sub R{my($p)[email protected]_;map R($_),<$p/*> if -d$p;($b=$p)=~s|.*/||;print"$p\n" if$H{$b}}R"."' f 

1-創建散列映射的鍵是文件名:%H = {地圖終日啃食; $ _ => 1} <>

2 - 定義一個遞歸子程序來遍歷目錄:子環R {}

2.1- recusive呼叫爲目錄:地圖R($ _),如果-d $ p

2.2-從路徑中提取文件名:($ b = $ p)=〜s |。*/||

2.3-打印如果散列映射包含文件名:打印 「$ P \ n」 個如果$ H {$ B}

3-呼叫R 2與路徑當前目錄: 「」 R

編輯:遍歷隱藏目錄

perl -e '%H=map{chomp;$_=>1}<>;sub R{my($p)[email protected]_;map R($_),grep !m|/\.\.?$|,<$p/.* $p/*> if -d$p;($b=$p)=~s|.*/||;print"$p\n" if$H{$b}}R"."' f 
+0

好而快,但由於某種原因它錯過了幾個文件 – moatPylon

+0

是否因爲文件名可以包含像*這樣的模式? –

+0

不是。事實上,我看不到缺少文件名的模式,我可以確認它們確實存在並被列出。 – moatPylon

0

根據目錄樹的百分比被認爲是匹配的,它可能會更快找到文件,然後用grep出匹配(*)。的:

find "$dir" -type f | grep -f <(sed 's+\(.*\)+/\1$+' "$f") 

sed命令預先處理您的文件名列表爲正則表達式,將只在路徑的終點相匹配的全名。

+0

將此運行停留幾分鐘,完全沒有輸出。對每個組件進行定時顯示,grep是罪魁禍首,並且,鑑於grep輸出一找到就匹配的事實,我不認爲這比我天真的方法更快。 – moatPylon

+0

太糟糕了。我甚至想到了一長串文件名,一個'grep'可能比許多對'find'的調用要快。 – chepner

0

下面是使用bash和grep

#!/bin/bash 

flist(){ 
for x in "$1"/*; do #*/ for markup 
[ -d "$x" ] && flist $x || echo "$x" 
done 
} 

dir=/etC#the directory you are searching 
list=$(< myfiles) #the file with file names 

#format the list for grep 
list="/${list// 
/\$\|/}" 

flist "$dir" | grep "$list" 

替代...如果您需要完整的POSIX外殼順應性(busybox的灰,噓,等...)替換$列表操縱子串用的變體chepner's sed並用$(cat文件)替換$(<文件)

+0

chepner方法的問題不在於'find',而是'grep'的速度,它不是固定在你的版本中,它也包含兩個問題:'find dir -type f'是沒有理由重新實現,'grep「$ list」'會溢出命令行參數長度的限制 – moatPylon