I have a string in Bash and I want to replace parts of it that match a certain pattern that matches multi-line substrings with an output of a command/function executed with each matched substring as an argument.
The string that I have is the output of this command:
openssl s_client -showcerts -connect example.net:443
It looks like this (heavily edited for brevity and clarity):
…stuff…
-----BEGIN CERTIFICATE-----
…Base64
multiline
string…
-----END CERTIFICATE-----
…stuff…
-----BEGIN CERTIFICATE-----
…Base64
multiline
string…
-----END CERTIFICATE-----
…stuff…
I want to replace the sections starting with -----BEGIN CERTIFICATE-----
and ending with -----END CERTIFICATE-----
with the output of the following command:
openssl x509 -in certificate.crt -text -noout
This command takes an input which is the Base64 string in between the above two markers (including the markers) and outputs a textual representation of the Base64.
My desired substitution is one where the Base64 lines get replaced by the output of the above command.
This means I need:
- An ability to specify a multi-line pattern (the two markers and lines between them)
- An ability to use a Bash function as the replacer predicate
- The function writes the matched substring to a file
- The function then executes the above command with the temporary file at input
- The function deletes the file
- The function returns the output of the command
The desired output looks like this:
…stuff…
-----BEGIN CERTIFICATE-----
…Textual
multiline
output…
-----END CERTIFICATE-----
…stuff…
-----BEGIN CERTIFICATE-----
…Textual
multiline
output…
-----END CERTIFICATE-----
…stuff…
I have found a few solutions on how to do one and the other using sed but I was not able to combine them. Eventually, I managed to get this mix of Bash and Python, but I am interested in a pure Bash solution:
echo | openssl s_client -showcerts -connect example.net:443 | python3 -c "
import fileinput
import re
import subprocess
import os
stdin=''.join(fileinput.input())
def replace(match):
with open('temp.crt', 'w') as text_file:
print(match.group(), file=text_file)
process = subprocess.run(['openssl', 'x509', '-in', 'temp.crt', '-text', '-noout'], capture_output=True, text=True)
os.remove('temp.crt')
return '-----BEGIN CERTIFICATE-----\n' + process.stdout + '\n-----END CERTIFICATE-----'
stdout=re.sub(r'-----BEGIN CERTIFICATE-----(\n(.*\n)+?)-----END CERTIFICATE-----', replace, stdin)
print(stdout)
"
I am trying to get something similar to this pseudocode to work:
echo \
| openssl s_client -showcerts -connect example.net:443 \
| sed 's/-----BEGIN CERTIFICATE-----\n.*?-----END CERTIFICATE-----/$(openssl x509 -in \0 -text -noout)/e'
I am not precious about using sed for this, but I also tried with awk and perl and did not get anywhere. Maybe OpenSSL can do this and I don't even need the substitution? Haven't found anything on that.
Is there a Bash one-line which can do all this?