过滤输出流以仅保留由特定行分隔的部分文本?
Filter output stream to only keep portion of text delimited by specific lines?
我已经创建了一个简单示例来说明我要完成的任务。本质上,我需要 运行 一个 shell 命令并捕获其输出,但只是其中的特定部分。以下面的 bash 命令为例:
> echo "hello\nhello\nstart\nI\nWANT\nTHIS\nTEXT\nend\nhello\n"
hello
hello
start
I
WANT
THIS
TEXT
end
hello
我预期的 common-lisp 输出将是 (list "I" "WANT" "THIS" "TEXT")
。我有一个半工作解决方案,但想知道如何获得 exact 所需的输出,以及是否有更惯用的方法来完成此任务。
首先,我创建一个闭包来跟踪我应该处理的行:
(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 语句启动我的程序,然后遍历输出流:
(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))))
哪个returns
(NIL NIL T "I" "WANT" "THIS" "TEXT" NIL NIL NIL)
如您所见,我不想要 T
和 NIL
值。我还必须使用我不太喜欢的 uiop:wait-process
,但我假设这是必需的。
更广泛的图片,我有大约 100 个这样的命令需要 运行 和解析。因此,我将同时关注 运行。这只是出于某种观点,我会 post 在一个单独的问题中。
> (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")
如果你想在一个地方完成所有工作,你可以使用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))
这里是 table 随着时间的推移绑定到每个循环变量的值,其中点表示 nil 以便于阅读。
|----------+-------+-------+-------+------+------|
| line | hello | start | text | text | end |
|----------+-------+-------+-------+------+------|
| previous | . | hello | start | text | text |
| start | . | . | T | . | . |
| end | . | . | . | . | T |
| active | . | . | T | T | . |
|----------+-------+-------+-------+------+------|
我已经创建了一个简单示例来说明我要完成的任务。本质上,我需要 运行 一个 shell 命令并捕获其输出,但只是其中的特定部分。以下面的 bash 命令为例:
> echo "hello\nhello\nstart\nI\nWANT\nTHIS\nTEXT\nend\nhello\n"
hello
hello
start
I
WANT
THIS
TEXT
end
hello
我预期的 common-lisp 输出将是 (list "I" "WANT" "THIS" "TEXT")
。我有一个半工作解决方案,但想知道如何获得 exact 所需的输出,以及是否有更惯用的方法来完成此任务。
首先,我创建一个闭包来跟踪我应该处理的行:
(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 语句启动我的程序,然后遍历输出流:
(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))))
哪个returns
(NIL NIL T "I" "WANT" "THIS" "TEXT" NIL NIL NIL)
如您所见,我不想要 T
和 NIL
值。我还必须使用我不太喜欢的 uiop:wait-process
,但我假设这是必需的。
更广泛的图片,我有大约 100 个这样的命令需要 运行 和解析。因此,我将同时关注 运行。这只是出于某种观点,我会 post 在一个单独的问题中。
> (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")
如果你想在一个地方完成所有工作,你可以使用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))
这里是 table 随着时间的推移绑定到每个循环变量的值,其中点表示 nil 以便于阅读。
|----------+-------+-------+-------+------+------|
| line | hello | start | text | text | end |
|----------+-------+-------+-------+------+------|
| previous | . | hello | start | text | text |
| start | . | . | T | . | . |
| end | . | . | . | . | T |
| active | . | . | T | T | . |
|----------+-------+-------+-------+------+------|