I have written an Expect script which spawns one subprocess (certbot), parses the output, then executes a second subprocess (a Python script) using data extracted from the first, and finally sends a carriage return (\r) to the first. I was recently debugging an issue with my script, so I thought I would add some logging to the Python script (which previously did not print any output). However, doing this caused the expect script to return this error:
while executing
"exec ./test2.py "$SITE_DOMAIN" "$fileName" "$fileData""
(file "./test.exp" line 23)
So then I tried changing my call to the second process / Python script from an exec call to a spawn call. This partially works and it got rid of the error. But strangely enough, I see the output from the first process right up until I start seeing output from the second process. Once the second process starts logging messages, I no longer see any output from the first.
I'm including here files to illustrate the problem as an MRE:
main.exp
#!/usr/bin/expect
set SITE_DOMAIN $env(SITE_DOMAIN)
set SITE_EMAIL $env(SITE_EMAIL)
# Spawn the certbot process
spawn ./test1.py certonly -m "$SITE_EMAIL" --agree-tos --no-eff-email --force-renew --preferred-challenges http --manual -d "$SITE_DOMAIN" -d "www.$SITE_DOMAIN"
set certbot_spawn_id $spawn_id
# Expect the output starting with 40 hyphens and capture it
expect {
-re {(?n)^(- ){39}-\r\nCreate a file containing just this data:\r\n\r\n([\w.-]+)\r\n\r\nAnd make it available on your web server at this URL:\r\n\r\n.+/acme-challenge/([\w.-]+)} {
set fileData $expect_out(2,string)
set fileName $expect_out(3,string)
}
timeout {
"Error: output from certbot did not match the expected pattern"
exit 1
}
}
# Call a separate command with the parsed value
exec ./test2.py "$SITE_DOMAIN" "$fileName" "$fileData"
# Send Enter to the certbot process to confirm
send -i $certbot_spawn_id "\r"
# Wait for the certbot process to finish
expect eof
test1.py
#!/usr/bin/env python
OUTPUT = """
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Account registered.
Requesting a certificate for example.com and www.example.com
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Create a file containing just this data:
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopq.rstuvwxyz01234567890ABCDEFGHIJKLMNOPQRSTUVW
And make it available on your web server at this URL:
http://example.com/.well-known/acme-challenge/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopq
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
"""
print(OUTPUT)
thing = input()
print("done")
test2.py
#!/usr/bin/env python
import logging
from time import sleep
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
logging.basicConfig()
logger.info("Uploading file to web server")
sleep(2)
logger.info("Upload complete")
sleep(1)
So if you run main.exp as-is, you'll see the first error that I mentioned. (And if you remove the logging statements from test2.py, those errors will go away.) If you change line 22 of main.exp to use spawn instead of exec, that too will fix the error, but then you won't see the word done output by the first process. The last thing displayed will be Upload complete.
loggingprints log messages to stderr.execraises error if it sees any output to stderr.Example:
Tcl's
exechas option-ignorestderrto stop it from raising error in such cases:Regarding "but then you won't see the word
doneoutput by the first process", you spawned a new process and then you also need to wait for it to complete:From Tcl's
execdoc: