Эквивалент find2perl в Python

В Perl есть прекрасная небольшая утилита под названием find2perl, которая переводит (довольно точно) командную строку для утилиту Unix find в сценарий Perl, чтобы сделать то же самое.

Если у вас есть такая команда find:

find /usr -xdev -type d -name '*share'

                         ^^^^^^^^^^^^  => name with shell expansion of '*share'
                 ^^^^ => Directory (not a file)
           ^^^ => Do not go to external file systems
     ^^^ => the /usr directory (could be multiple directories

Он находит все каталоги, заканчивающиеся на share ниже /usr

Теперь запустите find2perl /usr -xdev -type d -name '*share', и он выдаст Perl-скрипт, который сделает то же самое. Затем вы можете изменить сценарий для своего использования.

В Python есть os.walk(), который, безусловно, имеет необходимую функциональность, рекурсивный список каталогов, но есть большие различия.

Рассмотрим простой случай find . -type f -print, чтобы найти и распечатать все файлы в текущем каталоге. Наивная реализация с использованием os.walk() выглядела бы так:

for path, dirs, files in os.walk(root):
    if files:
        for file in files:
            print os.path.join(path,file)

Однако это приведет к другим результатам, чем при вводе find . -type f -print в оболочке.

Я также тестировал различные циклы os.walk () против:

# create pipe to 'find' with the commands with arg of 'root'
find_cmd='find %s -type f' % root
args=shlex.split(find_cmd)
p=subprocess.Popen(args,stdout=subprocess.PIPE)
out,err=p.communicate()    
out=out.rstrip()            # remove terminating \n
for line in out.splitlines()
   print line

Разница в том, что os.walk () считает ссылки как файлы; find пропускает их.

Таким образом, правильная реализация, аналогичная file . -type f -print, становится:

for path, dirs, files in os.walk(root):
    if files:
        for file in files:
            p=os.path.join(path,file)
            if os.path.isfile(p) and not os.path.islink(p):
                 print(p)

Поскольку существуют сотни вариантов основных вариантов поиска и различных побочных эффектов, проверка каждого варианта занимает много времени. Поскольку find является золотым стандартом в мире POSIX по подсчету файлов в дереве, для меня важно делать то же самое в Python.

Так есть ли эквивалент find2perl, который можно использовать для Python? До сих пор я просто использовал find2perl, а затем вручную переводил код Perl. Это сложно, потому что операторы проверки файлов Perl отличаются, чем файл Python иногда проверяет в os.path.


person dawg    schedule 24.09.2011    source источник
comment
Я бы предположил, что часть ответа можно найти здесь: stackoverflow.com/questions/4639506/os-walk -with-regex Извините, я не знаю find / find2perl достаточно, чтобы помочь вам. (возможно, также stackoverflow.com/ questions / 5141437 /)   -  person James Khoury    schedule 30.09.2011


Ответы (4)


Есть пара наблюдений и несколько фрагментов кода, которые помогут вам на вашем пути.

Во-первых, Python может выполнять код в этой форме точно так же, как Perl:

 cat code.py | python | the rest of the pipe story...

find2perl - это продуманный шаблон кода, который генерирует функцию Perl на основе шаблона поиска. Поэтому воспроизведите этот шаблон, и у вас не будет «сотен перестановок», которые вы воспринимаете.

Во-вторых, результаты find2perl не идеальны, так же как есть потенциальные различия между версиями find, такими как GNU или BSD.

В-третьих, по умолчанию os.walk идет снизу вверх; find сверху вниз. Это дает разные результаты, если ваше базовое дерево каталогов изменяется во время его рекурсии.

Есть два проекта на Python, которые могут вам помочь: twander и dupfinder. Каждый стремится быть независимым от операционной системы, и каждый рекурсирует файловую систему, как find.

Если вы шаблонизируете общую find подобную функцию в Python, устанавливаете os.walk на рекурсию сверху вниз, используете glob для репликации расширения оболочки и используете часть кода, который вы найдете в этих двух проектах, вы можете реплицировать find2perl без особых трудностей.

Извините, я не смог указать на что-то готовое для ваших нужд ...

person the wolf    schedule 02.10.2011

Если вы пытаетесь заново реализовать find, то да, ваш код станет непростым. find сам по себе довольно волосат.

Однако в большинстве случаев вы не пытаетесь полностью воспроизвести поведение find; вы выполняете гораздо более простую задачу (например, «найти все файлы с расширением .txt»). Если вам действительно нужен весь find, просто запустите find и прочтите вывод. Как вы говорите, это золотой стандарт; вы можете просто использовать его.

Я часто пишу код, который считывает пути на stdin, чтобы я мог это сделать:

find ...a bunch of filters... | my_python_code.py
person larsks    schedule 25.09.2011
comment
Это работает только при условии, что ваша целевая программная среда находится на Unix tho. Прелесть find2perl в том, что вы можете написать что-нибудь в Unix и запустить это везде, где работает Perl - например, в Windows. - person the wolf; 25.09.2011

Я думаю, что glob может помочь вам в реализации этого.

person Benjamin    schedule 30.09.2011

Я написал сценарий Python для использования os.walk() для поиска и замены; это может быть полезно посмотреть, прежде чем писать что-то подобное.

Заменить строки в файлах Python

И любая замена Python для find (1) будет во многом полагаться на os.stat() для проверки различных свойств файла. Например, есть флаги для поиска (1), которые проверяют размер файла или последнюю измененную временную метку.

person steveha    schedule 30.09.2011