From: Zelphir Kaltstahl <zelphirkaltstahl@posteo.de>
To: Josselin Poiret <dev@jpoiret.xyz>
Cc: guile-user@gnu.org
Subject: Re: Shell commands with output to string
Date: Tue, 8 Mar 2022 23:12:25 +0000 [thread overview]
Message-ID: <3080f6d4-e341-13a8-81e1-a6c7f6a3efb2@posteo.de> (raw)
In-Reply-To: <87czjdn0l1.fsf@jpoiret.xyz>
Hello Josselin and all!
On 2/23/22 15:01, Josselin Poiret wrote:
> Hello,
>
>
> post@thomasdanckaert.be writes:
>
>> Hi,
>>
>> to throw in an example: I once used a function like the one below to
>> handle stdout and stderr from external commands (from
>> https://github.com/tdanckaert/jobview/blob/master/jobtools.scm#L38 ).
>> Probably far from perfect (my first and only scheme project...), but
>> hopefully it gives you an idea.
> Just chiming in to say that [1] isn't fixed yet, so you may run into
> issues if you try to redirect out and err to the same port. In Guix, we
> use the following workaround for now ([2]):
> --8<---------------cut here---------------start------------->8---
> (match-let (((input . output) (pipe)))
> ;; Hack to work around Guile bug 52835
> (define dup-output (duplicate-port output "w"))
> ;; Void pipe, but holds the pid for close-pipe.
> (define dummy-pipe
> (with-input-from-file "/dev/null"
> (lambda ()
> (with-output-to-port output
> (lambda ()
> (with-error-to-port dup-output
> (lambda ()
> (apply open-pipe* (cons "" command)))))))))
> (close-port output)
> (close-port dup-output)
> (handler input)
> (close-port input)
> (close-pipe dummy-pipe))
> --8<---------------cut here---------------end--------------->8---
>
> [1]https://debbugs.gnu.org/cgi/bugreport.cgi?bug=52835
> [2]https://git.savannah.gnu.org/cgit/guix.git/tree/gnu/installer/utils.scm?id=c0bc08d82c73e464a419f213d5ae5545bc67e2bf#n87
>
> Best,
I have questions regarding this workaround:
Can you explain how and why this works? I have tried to make sense of it and
here are my notes so far (reference:
https://notabug.org/ZelphirKaltstahl/guile-examples/src/2dead9f7bb9b40fc26eb490a93e1dc7abca7252c/shell/system-asterisk-stdout-to-stderr-redirection-bug.scm):
~~~~
(match-let (((input . output) (pipe)))
;; Hack to work around Guile bug 52835 -- How does
;; duplicating the port help? From the docs: "Returns a
;; new port which is opened on a duplicate of the file
;; descriptor underlying port, with mode string modes as
;; for open-file. The two ports will share a file position
;; and file status flags. [...]"
(define dup-output (duplicate-port output "w"))
;; Void pipe, but holds the pid for close-pipe.
(define dummy-pipe
;; Set current-input-port to /dev/null. -- What will be
;; read from there? Nothing?
(with-input-from-file "/dev/null"
(lambda ()
;; Set the current-output-port to the one created
;; above using (pipe).
(with-output-to-port output
(lambda ()
;; Set the error port to the duplicated output
;; port. This might be the redirection of stderr
;; to stdout.
(with-error-to-port dup-output
(lambda ()
;; Run open-file*, but why is there an empty
;; string prepended to command? Perhaps to
;; allow using either a list or a string as
;; a command?
(apply open-pipe* (cons "" command)))))))))
(close-port output)
(close-port dup-output)
(handler input)
(close-port input)
(close-pipe dummy-pipe))
~~~~
My other question is: Do I still need this workaround, if I use the following,
to run commands? And if so, why? In which cases would my code not do the right
thing? (reference:
https://notabug.org/ZelphirKaltstahl/guile-examples/src/2dead9f7bb9b40fc26eb490a93e1dc7abca7252c/shell/example-03-using-popen-get-out-and-error.scm):
~~~~
(import (ice-9 popen)
(ice-9 textual-ports)
(ice-9 exceptions)
(ice-9 receive)
(ice-9 match))
;; Removed comments to shorting this example. For more
;; explanation see the first example.
(define run-command
(λ (cmd)
"Runs CMD as an external process, with an input port
from which the process' stdout may be read."
(match-let ([(err-read . err-write) (pipe)]
[stderr (current-error-port)])
(with-error-to-port err-write
(λ ()
(let* (;; Run the actual command. If an error
;; happens, it should write to the
;; err-write port. Output of the command
;; should be written to an output port,
;; which corresponds to the input-port,
;; which is returned by open-input-pipe.
[in-port (open-input-pipe cmd)]
;; Read in block mode.
[_ignored (setvbuf in-port 'block)]
;; Get command output and error output.
[command-output (get-string-all in-port)]
;; Get the exit code of the command.
[exit-code (close-pipe in-port)])
;; Close the port, to which the child process
;; was to write errors, as the child process has
;; finished (either successfully or
;; unsuccessfully, but definitely finished).
(close-port err-write)
(let (;; Get the error message, if there is any.
[error-message (get-string-all err-read)])
(values exit-code
command-output
error-message))))))))
(receive (exit-code command-output error-message)
(let ([command "echo 'bong' 1>&2"])
(run-command command))
(display (simple-format #f "exit code: ~a\n" exit-code))
(unless (string-null? command-output)
(display (simple-format #f "command-output: \n~a" command-output)))
(unless (string-null? error-message)
(display (simple-format #f "error-message: \n~a" error-message))))
(receive (exit-code command-output error-message)
(let ([command "ls -al"])
(run-command command))
(display (simple-format #f "exit code: ~a\n" exit-code))
(unless (string-null? command-output)
(display (simple-format #f "command-output: \n~a" command-output)))
(unless (string-null? error-message)
(display (simple-format #f "error-message: \n~a" error-message))))
;; Both, output and error:
(receive (exit-code command-output error-message)
(let ([command "ls -al 2>&1 && echo 'bong' 1>&2"])
(run-command command))
(display (simple-format #f "exit code: ~a\n" exit-code))
(unless (string-null? command-output)
(display (simple-format #f "command-output: \n~a" command-output)))
(unless (string-null? error-message)
(display (simple-format #f "error-message: \n~a" error-message))))
~~~~
Best regards,
Zelphir
--
repositories:https://notabug.org/ZelphirKaltstahl
next prev parent reply other threads:[~2022-03-08 23:12 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-02-22 9:29 Shell commands with output to string Zelphir Kaltstahl
2022-02-22 9:38 ` Zelphir Kaltstahl
2022-02-22 10:20 ` Alex Sassmannshausen
2022-02-22 10:43 ` post
2022-02-23 14:01 ` Josselin Poiret
2022-03-08 23:12 ` Zelphir Kaltstahl [this message]
2022-03-09 14:14 ` Josselin Poiret
2022-02-22 11:20 ` Neil Jerram
2022-02-23 1:28 ` Zelphir Kaltstahl
2022-02-23 1:29 ` Zelphir Kaltstahl
2022-02-22 10:21 ` tomas
2022-02-22 14:27 ` Olivier Dion via General Guile related discussions
2022-02-22 16:00 ` Leo Butler
2022-02-22 16:33 ` Olivier Dion via General Guile related discussions
2022-02-23 1:26 ` Zelphir Kaltstahl
2022-02-23 14:13 ` Olivier Dion via General Guile related discussions
2022-02-26 0:32 ` Zelphir Kaltstahl
-- strict thread matches above, loose matches on Subject: below --
2022-02-23 17:48 Blake Shaw
2022-02-23 18:25 ` Olivier Dion via General Guile related discussions
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/guile/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=3080f6d4-e341-13a8-81e1-a6c7f6a3efb2@posteo.de \
--to=zelphirkaltstahl@posteo.de \
--cc=dev@jpoiret.xyz \
--cc=guile-user@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).