Есть ли способ достичь grep-подобной функциональности на выходе внутри Rails Console? - PullRequest
9 голосов
/ 05 августа 2011

В оболочке я могу сделать

$ cat name_of_file_with_a_lot_of_text | grep "What I am looking for"

Внутри консоли Rails могу ли я добиться чего-то похожего, скажем, когда я запускаю команду, и результат огромен, особенно, скажем, запрос БД.
Я знаю, что выводить его как YAML, но это не то, что я ищу.

Спасибо.

Ответы [ 2 ]

19 голосов
/ 05 августа 2011

Да, вы можете. Метод называется gr ... подожди ... ep. Ruby's grep работает на String, Array и многих других встроенных объектах. Например, чтобы получить все to_xxx методы числа, просто выполните:

 1.methods.grep(/to_/)
2 голосов
/ 18 июля 2012

У меня был тот же вопрос, и я не был очень доволен каким-то странным ответом от ream88, поэтому я решил попробовать его.

# Allows you to filter output to the console using grep
# Ex:
#   def foo
#     puts "Some debugging output here"
#     puts "The value of x is y"
#     puts "The value of foo is bar"
#   end
# 
#   grep_stdout(/value/) { foo }
#   # => The value of x is y
#   # => The value of foo is bar
#   # => nil
def grep_stdout(expression)
  # First we need to create a ruby "pipe" which is two sets of IO subclasses
  # the first is read only (which represents a fake $stdin) and the second is
  # write only (which represents a fake $stdout).
  placeholder_in, placeholder_out = IO.pipe

  # This child process handles the grep'ing.  Its done in a child process so that
  # it can operate in parallel with the main process.
  pid = fork {
    # sync $stdout so we can report any matches asap
    $stdout.sync

    # replace $stdout with placeholder_out
    $stdin.reopen(placeholder_in)

    # we have to close both placeholder_out and placeholder_in because all instances
    # of an IO stream must be closed in order for it to ever reach EOF.  There's two
    # in this method; one in the child process and one in the main process.
    placeholder_in.close
    placeholder_out.close

    # loop continuously until we reach EOF (which happens when all
    # instances of placeholder_out have closed)
    read_buffer = ''
    loop do
      begin
        read_buffer << $stdin.readpartial(4096)
        if line_match = read_buffer.match(/(.*\n)(.*)/)
          print line_match[1].grep(expression)  # grep complete lines
          read_buffer = line_match[2]           # save remaining partial line for the next iteration
        end
      rescue EOFError
        print read_buffer.grep(expression)  # grep any remaining partial line at EOF
        break
      end
    end
  }

  # Save the original stdout out to a variable so we can use it again after this
  # method is done
  original_stdout = $stdout

  # Redirect stdout to our pipe
  $stdout = placeholder_out

  # sync $stdout so that we can start operating on it as soon as possible
  $stdout.sync

  # allow the block to execute and save its return value
  return_value = yield

  # Set stdout back to the original so output will flow again
  $stdout = original_stdout

  # close the main instances of placeholder_in and placeholder_out
  placeholder_in.close
  placeholder_out.close

  # Wait for the child processes to finish
  Process.wait pid

  # Because the connection to the database has a tendency to go away when calling this, reconnect here
  # if we're using ActiveRecord
  if defined?(ActiveRecord)
    suppress_stdout { ActiveRecord::Base.verify_active_connections! }
  end

  # return the value of the block
  return_value
end

Очевидный недостаток моего решения - потеря вывода. Я не уверен, как обойти это, не звоня yield дважды.

РЕДАКТИРОВАТЬ Я изменил свой ответ на один вызов только fork один раз, что позволяет мне сохранить выходные данные блока и вернуть его в конце. Win.

РЕДАКТИРОВАТЬ 2 Вы можете получить все эти функциональные возможности (и даже больше!) В этом самоцвете сейчас https://github.com/FutureAdvisor/console_util

...