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

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

Если у вас есть команда 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 script, чтобы сделать то же самое. Затем вы можете изменить script на использование.

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.

Ответы

Ответ 1

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

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

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

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

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

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

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

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

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

Ответ 2

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

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

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

find ...a bunch of filters... | my_python_code.py

Ответ 3

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

Ответ 4

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

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

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