From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Zelphir Kaltstahl Newsgroups: gmane.lisp.guile.user Subject: Re: Shell commands with output to string Date: Tue, 8 Mar 2022 23:12:25 +0000 Message-ID: <3080f6d4-e341-13a8-81e1-a6c7f6a3efb2@posteo.de> References: <1280eba0-81c0-6e7b-d512-bbd1f83a409c@posteo.de> <87tucryzdg.fsf@gmail.com> <6d1232e036287a4cc7994d9fac5fceec@thomasdanckaert.be> <87czjdn0l1.fsf@jpoiret.xyz> Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="38854"; mail-complaints-to="usenet@ciao.gmane.io" Cc: guile-user@gnu.org To: Josselin Poiret Original-X-From: guile-user-bounces+guile-user=m.gmane-mx.org@gnu.org Wed Mar 09 00:13:22 2022 Return-path: Envelope-to: guile-user@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1nRj18-000A0J-0N for guile-user@m.gmane-mx.org; Wed, 09 Mar 2022 00:13:22 +0100 Original-Received: from localhost ([::1]:53876 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1nRj16-0000EE-Vp for guile-user@m.gmane-mx.org; Tue, 08 Mar 2022 18:13:21 -0500 Original-Received: from eggs.gnu.org ([209.51.188.92]:47312) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1nRj0J-0008Pg-UK for guile-user@gnu.org; Tue, 08 Mar 2022 18:12:32 -0500 Original-Received: from mout01.posteo.de ([185.67.36.65]:44777) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1nRj0H-0001ZT-4v for guile-user@gnu.org; Tue, 08 Mar 2022 18:12:31 -0500 Original-Received: from submission (posteo.de [185.67.36.169]) by mout01.posteo.de (Postfix) with ESMTPS id D737F240026 for ; Wed, 9 Mar 2022 00:12:26 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=posteo.de; s=2017; t=1646781146; bh=aiiCl/Y9QjrC5ZgHCQ6F1kCdjooH+Ejipm13PmUpL6I=; h=Date:Subject:To:Cc:From:From; b=ZeDeiMMJMqMqxymhkRn72LtzTYK9zfjAJTAT71xndOLKmCd5ZbaiozBGjQob3PoJL Lz9WipkejUH9Rn8PbYHiDO5CWgxRD/sl8WHkOKGadYZVLcepIdErnr0c8nEEq7ojag XWEqy5H0Kg+rgoMyPuYRteHeYpqluZMgwl0fIWKrACWp4yVijlVcRIMrfpmU+SBzhp 269HW+nDrduUPbY1FrkpTMmp6ZtkImbDn0qkAxpLiXPA7EKjfk0aWtcEkI7Y9iJsEz j4IqTdn1E7I8YQniVovJ+bUzCEyeJ0+d86lk/bISvWvXKeQw/7ernEXvcb42ARr7H+ thbwY4aJi9oIw== Original-Received: from customer (localhost [127.0.0.1]) by submission (posteo.de) with ESMTPSA id 4KCrh95XWBz9rxB; Wed, 9 Mar 2022 00:12:25 +0100 (CET) Content-Language: en-US In-Reply-To: <87czjdn0l1.fsf@jpoiret.xyz> Received-SPF: pass client-ip=185.67.36.65; envelope-from=zelphirkaltstahl@posteo.de; helo=mout01.posteo.de X-Spam_score_int: -43 X-Spam_score: -4.4 X-Spam_bar: ---- X-Spam_report: (-4.4 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, HTML_MESSAGE=0.001, RCVD_IN_DNSWL_MED=-2.3, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-Content-Filtered-By: Mailman/MimeDel 2.1.29 X-BeenThere: guile-user@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: General Guile related discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guile-user-bounces+guile-user=m.gmane-mx.org@gnu.org Original-Sender: "guile-user" Xref: news.gmane.io gmane.lisp.guile.user:18169 Archived-At: 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