Лучшая практика для рекурсивного консольного инструмента в Python

Какова наилучшая практика (интерфейс и реализация) для инструмента командной строки, который обрабатывает выбранные файлы в дереве каталогов?

Я приведу пример, который приходит мне на ум, но я ищу «лучшую практику»:

flipcase foo.txt foo2.txt 

может обрабатывать файл foo.txt и сохранять результат как foo2.txt.

 flipcase -rv *.txt 

может обрабатывать все текстовые файлы в текущем каталоге.
-r или --recursive будет включать все подкаталоги.
-v будет печатать некоторую информацию в stdout во время обработки.

Одна из проблем, которые я вижу в этом примере, заключается в том, что аргумент *.txt иногда расширяется оболочкой (Unix и Vista), поэтому я не могу применить этот шаблон при переходе по вспомогательным каталогам.
Я думаю, причина в том, что в Unix такие инструменты придуманы с призывом find , но это, похоже, не распространено в Windows. Это также затрудняет распечатку резюме в конце.

Требования:

  • ДОЛЖЕН работать в Unix, Windows XP, Windows 7 и Mac
  • СЛЕДУЕТ следовать общим соглашениям на этих платформах. (Да, я знаю, но я ищу разумный компромисс. Например, это удобно использовать - вместо / на Windows.)
  • НЕ ДОЛЖНО полагаться на отдельную команду find, как это делает grep.
  • ДОЛЖНА работать для одиночных файлов, шаблонов файлов и шаблонов в иерархиях каталогов.
  • СЛЕДУЕТ строить со стандартными библиотеками Python, например OptionParser и os.walk .
  • COULD обрабатывать несколько шаблонов, например *.txt,*.html .

Другие вопросы, касающиеся проектных решений:

  • Что должен вернуть этот инструмент (код состояния)?
  • Какие ctrl-ключи должны обрабатывать этот инструмент и каким образом?
  • Следует ли поддерживать stdin вместо одного файла? Конфигурируемое или автоматическое обнаружение?
  • Должна ли поддерживаться перенаправление вывода? Конфигурируемое или автоматическое обнаружение? Как работать с отладочной выводом в этом случае?
  • Должен ли шаблон быть глобальным синтаксисом или регулярным выражением?
  • Есть ли общий синтаксис шаблонов, который поддерживает рекурсию? Возможно, recursive:*.txt В этом случае опция -r не обязательна.
  • Что лучше всего создавать резервные копии измененных файлов? Вариант -b , или, скорее, резервные копии по умолчанию и добавьте опцию --no-backup
  • Для отдельных файлов должно быть возможно указать имя целевого файла. Как?
  • Какую информацию о состоянии нужно распечатать и настроить эту настройку? Должен ли он быть подробным по умолчанию, и мы разрешаем -q для спокойствия? Или всегда печатайте немного и разрешите -v (или -vv ), чтобы увеличить это или -q чтобы полностью заткнуться?

Я действительно не ожидаю получить один правильный ответ, но может быть кучей мыслей и указателей на хорошие примеры проектов.

По моему опыту, лучшей отправной точкой является создание инструмента, который следует основным принципам Unix, а именно: чтение со стандартного ввода и запись на стандартный вывод. Это позволяет людям использовать ваш инструмент гибким способом:

 flipcase input.txt > output.txt othercommand | flipcase > output.txt flipcase | othercommand > ouput.txt flipcase input1.txt input2.txt > output.txt 

Следующей функцией может быть редактирование на месте:

 # Modify input files directly. flipcase -i input.txt # Create backup copies before modifying originals. flipcase -i --backup-suffix '_BAK' input.txt flipcase -i --backup-prefix 'BAK_' input.txt # Regex for power users. flipcase -i --backup-regex 's/foo/bar/' input.txt 

В сложном режиме инструмент не должен записываться на стандартный вывод, потому что это противоречило бы основным принципам выше. Он должен писать стандартную ошибку или пользовательский файл журнала.

 flipcase -v input.txt > output.txt flipcase -v log.txt input.txt > output.txt 

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

 flipcase -r -i --backup-suffix '_BAK' --filter-glob '*.txt' dir1 dir2 flipcase -r -i --backup-suffix '_BAK' --filter-glob '*.txt' --filter-glob 'log*.dat' dir flipcase -r -i --backup-suffix '_BAK' --filter-regex 'log\w+\.(txt|log)$' dir1 dir2 # Don't do in-place editing. Instead create new files within the structure. flipcase -r --newname-suffix '_NEW' --filter-glob '*.txt' dir1 dir2 flipcase -r --newname-regex 's/\.txt$/_new.txt/' --filter-glob '*.txt' dir1 dir2 # Create the backups or the new files in a parallel directory # structure rather than within the original structure. flipcase -r -i --backup-tree 'backup_dir' --filter-glob '*.txt' dir1 dir2 flipcase -r -i --new-tree 'newfiles_dir' --filter-glob '*.txt' dir1 dir2 

Чтобы обратиться к части вашего запроса, странный человек в вашем списке действительно поддерживает Windows. Путь UNIX, а также хороший способ сделать это – позволить оболочке обрабатывать глобусы. Вы просто получите список файлов. Я не знаю инструмента UNIX, что делает его собственное подтачивание (в таких основных случаях). Я бы посоветовал вам не делать этого сам, но полагаться на оболочку.

В Windows вы можете ссылаться на людей с помощью оболочки с Cygwin или что-то в этом роде. Конечно, пользователи Windows обычно избегают командной строки, поэтому, если вы создадите графический интерфейс, они тоже будут счастливы.

Это не распространяется на ваш -r . Но там трудно. Вы хотите предоставить пользователям возможность указывать «все файлы в подкаталогах с расширением .txt»? Обратите внимание, что современные оболочки, такие как ZSH, могут делать глобусы, которые записываются в каталоги, например:

 rm **/*.tmp 

и, как вы говорите, вы всегда можете использовать find . Поэтому рекомендация здесь действительно должна учитывать специфику вашего инструмента. rsync извлекает выгоду из реализации своего собственного ключа -r , но гипотетический flipcase вероятно, не будет.

Какова наилучшая практика (интерфейс и реализация) для инструмента командной строки, который обрабатывает выбранные файлы в дереве каталогов?

Я не думаю, что существует один стандарт или «лучшая практика», когда дело доходит до реализации инструмента командной строки. Хотя, вы получите много идей, посмотрев и экспериментируя с хорошо подобранными инструментами, например, GNU coreutils.

Кроме того, я думаю, что вы ищете что-то подобное: http://www.gnu.org/prep/standards/html_node/Command_002dLine-Interfaces.html

Чтение и экспериментирование в отношении способа Unix для этого действительно затрагивают многие из ваших проблем, связанных с дизайнерскими решениями.

Одна из проблем, которые я вижу в этом примере, заключается в том, что аргумент * .txt иногда расширяется оболочкой (Unix и Vista), поэтому я не могу применить этот шаблон при переходе по вспомогательным каталогам.

В Unix * автоматически расширяется. Я не уверен в Windows, но если я не ошибаюсь, * не расширяется, поэтому вы можете просто использовать glob.glob(sys.argv[1]) . Обходной путь для Unix заключается в том, чтобы избежать шаблона, но должен быть лучший способ.

Рекурсивная обработка обычно выполняется с помощью os.path.walk , но вы можете создать свою собственную версию для использования генераторов Python, которая намного удобнее для командной строки: piping получит результат по мере его обработки. Вот проверенное и документированное доказательство концепции .

С Python 3 вам не нужно это делать, поскольку он обеспечивает os.walk, которые создают генератор.

Затем, следуйте советам FM, чтобы создать интерфейс CLI с помощью optparse .