Рекурсивное сопоставление имен файлов с аргументом glob

Я пытался получить список файлов, соответствующих шаблону глобуса в аргументе командной строки (sys.argv[1]), рекурсивно используя glob.glob и os.walk. Проблема в том, что bash (и, кажется, многие другие оболочки) автоматически расширяют шаблоны глобусов в имена файлов.

Как это делают стандартные программы unix (например, grep -R)? Я понимаю, что они не в питоне, но если это происходит на уровне оболочки, это не должно иметь значения, верно? Есть ли способ для скрипта сообщить оболочке, что не следует автоматически расширять шаблоны глобусов? Похоже, что set -f отключит подстановку, но я не уверен, как запустить это достаточно рано, так сказать.

Я видел Используйте Glob() для рекурсивного поиска файлов в Python?, но это не распространяется на фактическое получение шаблонов глобусов из аргументов командной строки.

Спасибо!

Редактировать:

Подобный grep скрипт perl ack принимает регулярное выражение perl в качестве одного из своих аргументов. Таким образом, ack .* выводит каждую строку каждого файла. Но .* должен распространяться на все скрытые файлы в каталоге. Я пробовал читать сценарий, но я не знаю perl; как это можно сделать?


person Bryan Head    schedule 23.05.2011    source источник


Ответы (3)


Оболочка выполняет расширение глобуса еще до того, как подумает о вызове команды. Такие программы, как grep, ничего не делают для предотвращения подстановки: они не могут. Вы, как вызывающая сторона этих программ, должны сообщить оболочке, что вы хотите передать программе специальные символы, такие как * и ?, и не позволять оболочке интерпретировать их. Вы делаете это, помещая их в кавычки:

grep -E 'ba(na)* split' *.txt

(ищите ba split, bana split и т. д. во всех файлах с именем ‹something›.txt) В этом случае помогут либо одинарные, либо двойные кавычки. Между одинарными кавычками оболочка ничего не расширяет. Между двойными кавычками $, ` и \ по-прежнему интерпретируются. Вы также можете защитить один символ от расширения оболочки, поставив перед ним обратную косую черту. Необходимо защищать не только подстановочные знаки; например, выше пробел в шаблоне заключен в кавычки, поэтому он является частью аргумента grep, а не разделителем аргументов. Альтернативные способы написания приведенного выше фрагмента включают

grep -E "ba(na)* split" *.txt
grep -E ba\(na\)\*\ split *.txt

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

grep b[an]*a *.txt

имеет различный эффект в зависимости от того, какие файлы присутствуют в системе. Если текущий каталог не содержит ни одного файла, имя которого начинается с b, команда ищет шаблон b[an]*a в файлах, имя которых соответствует *.txt. Если текущий каталог содержит файлы с именами baclava, bnm и hello.txt, команда расширяется до grep baclava bnm hello.txt, поэтому она ищет шаблон baclava в двух файлах bnm и hello.txt. Излишне говорить, что полагаться на это в сценариях — плохая идея; в командной строке иногда можно сэкономить на наборе текста, но это рискованно.

Когда вы запускаете ack .* в каталоге, не содержащем точечный файл, оболочка запускает ack . ... Поведение команды ack состоит в том, чтобы рекурсивно распечатать все непустые строки (шаблон .: соответствует любому одному символу) во всех файлах под .. (родительским для текущего каталога). В отличие от ack '.*', который ищет шаблон .* (который соответствует чему-либо) в текущем каталоге и его подкаталогах (из-за поведения ack, когда вы не передаете аргумент имени файла).

person Gilles 'SO- stop being evil'    schedule 24.05.2011
comment
Смотрите мою правку. Как тогда программа ack может принимать регулярное выражение Perl без кавычек или обратной косой черты? - person Bryan Head; 24.05.2011
comment
@Bryan: Это не так, посмотрите на вывод более внимательно (или посмотрите мое редактирование). - person Gilles 'SO- stop being evil'; 24.05.2011

Когда дело доходит до grep, он просто принимает список имен файлов и не выполняет само расширение глобуса. Если вам действительно нужно передать шаблон в качестве аргумента, его нужно заключить в командную строку в одинарные кавычки. Но прежде чем вы это сделаете, подумайте о том, чтобы позволить оболочке выполнять работу, для которой она была разработана.

person Adam Byrtek    schedule 23.05.2011
comment
А, я вижу, это хороший момент. Я использовал grep в течение многих лет и никогда не замечал, что он на самом деле ничего не делает с глобусами, подобными шаблонам (то же самое для других команд unix). А, хорошо, спасибо! - person Bryan Head; 24.05.2011
comment
Это соответствует философии Unix, согласно которой каждый инструмент должен нести отдельную ответственность. - person Adam Byrtek; 24.05.2011

Да, set -f, вы на правильном пути.

Похоже, вы собираетесь вызывать свою программу на Python из оболочки.

Каждый раз, когда вы используете оболочку для выполнения команды, она пытается сканировать строку cmd и обрабатывает подстановочные знаки, подстановку команд и множество других вещей.

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

set -f
echo *
*

myprogram *.txt

передаст вашей программе строку '*.txt'. Затем вы можете использовать внутреннюю подстановку для получения ваших файлов.

ИЛИ вы можете сделать то же самое, создав скрипт-оболочку

 #!/bin/bash
 set -f
 myProgram ${@}

где ${@} are the arguments you pass in when you startmyProgram` либо из командной строки, crontab, либо через exec(...) из другого процесса.

Надеюсь, это поможет.

person shellter    schedule 23.05.2011
comment
Вы имеете в виду сначала явно запустить run set -f в оболочке, а затем запустить программу? Я предполагал, что обернуть программу python в сценарий bash, который сначала вызовет set -f, не сработает... Ну что ж, спасибо! - person Bryan Head; 24.05.2011
comment
Только что попробовал, и, увы, программа все равно получила расширенные имена файлов. То же самое произошло, когда я заменил myProgram на echo ${@}; он печатал имена файлов, а не глобус. - person Bryan Head; 24.05.2011
comment
доа. Да, ${@} получит аргументы из строки cmd, $1, $2 ... $n, что означает, что эти значения уже расширены. Итак, предыдущий комментарий (которого я сейчас не вижу) нужно отправить аргументами, заключенными в одинарные кавычки, т.е. myWrapper ... '*'... Удачи! - person shellter; 24.05.2011