Фильтровать выходной поток, чтобы сохранить только часть текста, разделенную указанными c строками? - PullRequest
1 голос
/ 23 апреля 2020

Я создал простой пример того, что я пытаюсь выполнить sh. По сути, мне нужно запустить команду оболочки и захватить ее вывод, но только определенную c ее часть. Возьмем, к примеру, следующую команду bash:

> echo "hello\nhello\nstart\nI\nWANT\nTHIS\nTEXT\nend\nhello\n"
hello
hello
start
I
WANT
THIS
TEXT
end
hello

Мой ожидаемый вывод с общим списком будет (list "I" "WANT" "THIS" "TEXT"). У меня есть полу-рабочее решение, но мне интересно, как я могу получить мой точный желаемый результат и есть ли более идиоматический c способ решения этой задачи.

Сначала я создаю замыкание для отслеживания строк, которые я должен обрабатывать:

(defun make-pre-processor ()
  (let ((proc-lines nil))
    #'(lambda (str)
        (cond
          ((string= str "start") (setf proc-lines t))
          ((string= str "end") (setf proc-lines nil))
          (proc-lines str)))))

Затем я использую оператор let для запуска моей программы, а затем l oop через выходной поток :

(let* ((input (concatenate 'string
                "hello\\n" "hello\\n" "start\\n"
                "I\\n" "WANT\\n" "THIS\\n" "TEXT\\n"
                "end\\n" "hello\\n"))
        (command (concatenate 'string "echo " "\"" input "\""))
        (*proc* (uiop:launch-program command :output :stream))
        (stream (uiop:process-info-output *proc*))
        (take-lines? (make-pre-processor)))
  (uiop:wait-process *proc*)
  (loop while (listen stream) collect
    (funcall take-lines? (read-line stream))))

Что возвращает

(NIL NIL T "I" "WANT" "THIS" "TEXT" NIL NIL NIL)

Как видите, есть значения T и NIL, которые мне не нужны. Я также должен использовать uiop:wait-process, который мне не особенно нравится, но я предполагаю, что это необходимо.

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

Ответы [ 2 ]

3 голосов
/ 23 апреля 2020
> (loop for e in '(NIL NIL T "I" "WANT" "THIS" "TEXT" NIL NIL NIL)
        when (stringp e)
        collect e)
("I" "WANT" "THIS" "TEXT")

Также это:

CL-USER 17 > (defun skip-lines-until (stream stop-line)
               (loop for line = (read-line stream nil)
                     while (and line
                                (not (string= line stop-line)))))
SKIP-LINES-UNTIL

CL-USER 18 > (defun collect-lines-until (stream stop-line)
               (loop for line = (read-line stream nil)
                     while (and line (not (string= line stop-line)))
                     collect line))
COLLECT-LINES-UNTIL

CL-USER 19 > (let ((lines "hi
there
start
1
2
3
stop
more
here"))
               (with-input-from-string (stream lines)
                 (skip-lines-until stream "start")
                 (collect-lines-until stream "stop")))
("1" "2" "3")
2 голосов
/ 24 апреля 2020

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

(with-open-file (in "/tmp/example")
  (loop
    for previous = nil then line
    for line = (read-line in nil nil)
    for start = (equal previous "start")
    for end = (equal line "end")
    for active = nil then (and (not end) (or active start))
    while line
    when active collect line))

Вот таблица того, какое значение привязано к каждому l oop переменная во времени, где точка обозначает ноль для удобства чтения.

|----------+-------+-------+-------+------+------|
| line     | hello | start | text  | text | end  |
|----------+-------+-------+-------+------+------|
| previous | .     | hello | start | text | text |
| start    | .     | .     | T     | .    | .    |
| end      | .     | .     | .     | .    | T    |
| active   | .     | .     | T     | T    | .    |
|----------+-------+-------+-------+------+------|
...