2014-11-07 3 views
1

Я обрабатываю файлы и каталоги, которые ищут самый последний измененный файл в каждом каталоге. Код, который у меня есть, но, будучи новым для Ruby, я неправильно исправляю ошибки.Как обрабатывать исключения в Find.find

Я использую Find.find получить рекурсивный список каталогов, называя свою собственную функцию newestFile для каждого каталога:

Find.find(ARGV[0]) { |f| 
    if File.directory?(f) 
    newestFile(f) 
    end 
} 

В дереве каталогов есть папки у меня нет разрешения на доступ, поэтому я хочу, чтобы игнорировать их и перейти к следующему, но я не вижу, как включить обработку исключений в цикл «Find.find».

Я попытался поместить begin..rescue..end вокруг блока, но это не позволяет мне продолжить обработку цикла.

Я также нашел этот вопрос: How to continue processing a block in Ruby after an exception?, но который обрабатывает ошибку в петля. Я пытаюсь восстановить из ошибок, возникающих в Find.find, которые будут вне блока исключений.

Вот трассировки стека ошибки:

PS D:\dev\ruby\> ruby .\findrecent.rb "M:/main/*" 
C:/Ruby200/lib/ruby/2.0.0/find.rb:51:in `open': Invalid argument - M:/main/<A FOLDER I CAN'T ACCESS> (Errno::EINVAL) 
     from C:/Ruby200/lib/ruby/2.0.0/find.rb:51:in `entries' 
     from C:/Ruby200/lib/ruby/2.0.0/find.rb:51:in `block in find' 
     from C:/Ruby200/lib/ruby/2.0.0/find.rb:42:in `catch' 
     from C:/Ruby200/lib/ruby/2.0.0/find.rb:42:in `find' 
     from ./findrecent.rb:17:in `<main>' 

Как добавить обработку исключений в этом коде?

Я заглянула в коде, где исключение генерируется и способ содержит следующий блок:

if s.directory? then 
    begin 
    fs = Dir.entries(file) 
    rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG 
    next 
    end 
    ... more code 

Выполнение ужасный хак я добавил Errno::EINVAL к списку rescue ошибок. Мой код теперь выполняется и проходит через все папки, но я не могу оставить это изменение в коде библиотеки Ruby.

Внутренне find использует Dir.entries, поэтому, возможно, мне нужно переписать код для обработки в другие папки самостоятельно, а не полагаться на find.

Я все еще хотел бы знать, есть ли способ обработки ошибок в такой конструкции кода, как от чтения другого кода. Этот тип мелкого/сжатого кода используется много в Ruby.

ответ

2

Получаете ли вы эту ошибку в своей функции newestFile или когда вы пытаетесь запустить File#directory??

Если это происходит в newestFile вы можете сделать что-то вроде этого:

Find.find(ARGV[0]) do |f| 
    if File.directory?(f) 
    newestFile(f) rescue nil 
    end 
end 

Это просто игнорирует любые ошибки и плоскодонки до следующей папке. Вы можете также сделать некоторые более хороший выход, если желательно:

Find.find(ARGV[0]) do |f| 
    if File.directory?(f) 
    begin 
     newestFile(f) 
    rescue 
     puts "error accessing: #{f}, you might now have permissions" 
    end 
    end 
end 

Если ошибка происходит в File#directory? вам нужно обернуть этот раздел, а также:

Find.find(ARGV[0]) do |f| 
    begin 
    if File.directory?(f) 
     newestFile(f) 
    end 
    rescue 
    puts "error accessing: #{f}, you might now have permissions" 
    end 
end 

Как вы упомянули, если ошибка происходит в Find#find сам тогда вы не можете поймать это из блока. Это должно было произойти внутри этого метода.

Можете ли вы подтвердить, что исключение происходит в этом методе, а не в последующих, путем вставки трассировки стека исключения?

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

Я собирался предложить обходе каталоги себя чем-то вроде Dir#entries, так что вы бы эту способность, чтобы поймать ошибки тогда. Меня интересует только то, что вы выходите из * в вызове из командной строки. Я нахожусь в MacOS, поэтому я не могу дублировать 100% то, что вы видите, но если я разрешаю ему перемещаться по каталогу, к которому у меня нет доступа на моем mac, он выводит отладочную информацию о том, какие папки я не могу получить, но продолжает на. Если я даю это *, с другой стороны, это ничего не делает, кроме как распечатать ошибку первой папки, к которой он не может получить доступ.

Одно из отличий в моем опыте работы с MacOS заключается в том, что на самом деле это не исключение, это просто печать этой информации об отладке на консоль. Но было интересно, что включение * my mine полностью прекратилось, если у меня не было доступа к папке.

+0

Я обновил свой вопрос с помощью трассировки стека. К сожалению, ошибка возникает в коде 'Find.find'. – Tony

+0

Спасибо за ваши предложения. Ваш комментарий: «Это должно произойти внутри этого метода». заставило меня подумать о дальнейшем расследовании, поэтому я посмотрел код 'find', и я добавил больше к моему вопросу. – Tony

+0

Ruby 'Find' специально написан для той цели, к которой использует OP, для быстрого пересечения иерархии каталогов и выборочной обработки файлов или каталогов на основе критериев. 'Dir.entries' более общий и * может * использоваться аналогичным образом, но приведет к большему количеству кода. –

1

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

Вместо того, чтобы ждать возникновения проблемы, пытаясь обработать исключение, вы можете узнать, нужно ли вам пытаться перейти в каталог или получить доступ к файлу с помощью методов класса owned? и grpowned? класса File. Из File документации:

grpowned?(file_name) → true or false 

Returns true if the named file exists and the effective group id of the calling process is the owner of the file. Returns false on Windows.

owned?(file_name) → true or false 

Returns true if the named file exists and the effective used id of the calling process is the owner of the file.

Это означает, что ваш код может выглядеть следующим образом:

Find.find(ARGV[0]) do |f| 
    if File.directory?(f) && %w[grpowned? owned?].any?{ |m| File.send(m.to_s, f) } 
    newestFile(f) 
    end 
end 

рубин будет проверять, если запись каталога является каталогом и есть ли он принадлежит или запущен текущим процессом. Поскольку && является короткозамкнутым, если он не является каталогом, второй набор тестов не будет запущен.

В некоторых системах групповые разрешения дадут вам больше шансов получить права доступа, если есть много общих ресурсов, поэтому сначала проверяется, а если он возвращает true, any? вернет true, и код будет прогрессировать. Если false возвращается, потому что групповые разрешения не разрешают доступ, тогда owned? проверит файл, и код пропустит или перейдет в newestFile. Переверните эти два теста на скорость в зависимости от настройки вашей системы. Или запустите код один с помощью time ruby /path/to/your/code, затем закрутите два и запустите его снова. Сравните полученные результаты, чтобы узнать, что быстрее в вашей системе.

Существуют разные школы мысли о том, хорошо ли использование обработки исключений для управления потоком программы, а разные языки предпочитают разные вещи в своих стилях программирования. Мне кажется, что код всегда будет работать быстрее и безопаснее, если я заранее знаю, смогу ли я что-нибудь сделать, а не попытаться взорвать его. Если он взрывается ожидаемым образом, это одно, но если он взрывается так, как я этого не ожидал, то у меня могло бы не быть обработки исключений, чтобы реагировать правильно, иначе это может вызвать другие исключения, которые маскируют истинную причину , Я бы лучше посмотрел, смогу ли я выйти из ситуации, проверив состояние, а затем, если все мои попытки не удались, у меня есть обработчик исключений, который позволяет мне изящно выйти. YMMV.

Наконец, в Ruby мы не называем методы с помощью Camelcase, мы используем snake_case. snake_case_is_easier toReadThanCamelCase.

+0

Мне нравится этот подход проверки собственности, прежде чем что-то делать с ним. – Bala

+0

Я думаю, это «добрее и нежнее». –

+0

@theTinMan - Спасибо за ваш ответ; Я также предпочитаю избегать исключений, описанных вами, но в этом случае исключение возникает в методе 'Find.find', поэтому я не могу проверить доступ к папкам в блоке до того, как он пойдет. В библиотеке Ruby может возникнуть ошибка, связанная с Windows, поскольку она восстанавливается из 'Errno :: EACCES' [Permission denied], но я получаю исключение Errno :: EINVAL [Invalid argument]. Я мог бы попытаться использовать перечислитель, возвращенный из 'find' (и не пропускать блок), или использовать' Dir.entries', и, как вы сказали в другом комментарии, живем с раздуванием кода. – Tony

Смежные вопросы