2

I'm trying to make a bash shell script that mounts, from some pre-obtained variables, a text file.

For this I use sed, changing the flags of the file by the desired:

sed "s/this is a line in text/$var/g" file

It works correctly and without problems. However, the $var variable often has strange characters, often being cryptographic keys such as:

-----BEGIN CERTIFICATE-----
MIID0DCCArigAwIBAgIJAMZxe+Z+DbdtMA0GCSqGSIb3DQEBCwUAME4xCzAJBgNV
BAYTAi0tMQswCQYDVQQIEwItLTELMAkGA1UEBxMCLS0xCzAJBgNVBAoTAi0tMRgw
FgYJKoZIhvcNAQkBFgltZUBteS5vcmcwHhcNMjEwODI1MDgyODMzWhcNMzEwODIz
MDgyODMzWjBOMQswCQYDVQQGEwItLTELMAkGA1UECBMCLS0xCzAJBgNVBAcTAi0t
MQswCQYDVQQKEwItLTEYMBYGCSqGSIb3DQEJARYJbWVAbXkub3JnMIIBIjANBgkq
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1VtgDdOnhyGGd3OQO+QHcqfiH1wP1wJC
5MPZFm9FOQKC74FscbeLflu+hXVSLQQjP8XgJrbCk+xNe+SFOLMoIG mpxvRsjZU
eLzA5lUR8PvmBvgt1iR s1AQQAyh6R7z5QdwamsjmyoE1Si6maRzeCiv46qmlyet
iUEqbXslMk8N6Pa3KsKskv7BgzSOlfLHuWaZScewViGBSbyKUUYV0ljWbGozs21i
w FftSM6JnyNIq6l0wvGYkpJoDpGyxeNPTykswSO6WsG5o8ogJYOQR3KduUqdalj
tHbBsnGB1PNqfhpkBn75FY 8aNryND+uYkkQu1fGVJG0j2XVcPSPpBywZSwGtcCO
LPTL9OIYlRzzVi vTS CRx4NqSY=
-----END CERTIFICATE-----

When this happens, the sed command breaks, resulting in:

sed: -e expression #1, char 39: unterminated `s' command

I understand that the contents of the variable is interpreted by sed as part of the sed command, giving the error.

Is there a way to send the variable to sed as plain text and that it is not interpreted?if is not possible, is there any simple alternative to replace sed in this case?


Update with proof that the content of the certificate has the special character /.

cat ca.crt | grep "\/"

grep: warning: stray \ before /
5MPZFm9FOQKC74FscbeLflu+hXVSLQQjP8XgJrbCk+xNe+SFOLMoIG/mpxvRsjZU
eLzA5lUR8PvmBvgt1iR/s1AQQAyh6R7z5QdwamsjmyoE1Si6maRzeCiv46qmlyet
gUX/9Bunyu65hQ+h5kV4aWMRgnDH8HoTQsrJQd4eshHwsqgQA3+Oou4m6GLihkfC
QwsENypmlOiOeYvmxBC0X/w/2IeZMEDbmrMO0yUL6No9xQan7wKNFwIDAQABo4Gw
ggEBAJBDpHQBNlFoMjeQrH1szNe+30rp3ECCwXH4L+qKegobZ5/+joWSk84pRF0J
w/FftSM6JnyNIq6l0wvGYkpJoDpGyxeNPTykswSO6WsG5o8ogJYOQR3KduUqdalj
tHbBsnGB1PNqfhpkBn75FY/8aNryND+uYkkQu1fGVJG0j2XVcPSPpBywZSwGtcCO
LPTL9OIYlRzzVi/vTS/CRx4NqSY=
Fco Javier Balón
  • 1,144
  • 2
  • 11
  • 31
  • Closely related: [*What characters do I need to escape when using `sed` in a `sh` script?*](https://unix.stackexchange.com/q/32907/108618) – Kamil Maciorowski Jan 20 '23 at 13:34
  • 4
    Irrelevant to your main issue, but there is no need for `cat`, you can give `grep` a file, and there is also no need to escape a `/`, it doesn't have any special meaning in regular expressions, so you could just do `grep / ca.crt`. – terdon Jan 20 '23 at 13:58
  • 1
    Can you show us the full template you are using? While this is almost certainly possible with sed, it will be easier using more sophisticated tools, but we'd need the full picture to be able to help. – terdon Jan 20 '23 at 13:59
  • Related:  [Is it possible to escape regex metacharacters reliably with sed?](https://stackoverflow.com/q/29613304/3960947)  (on Stack Overflow). – G-Man Says 'Reinstate Monica' Jan 28 '23 at 18:35

3 Answers3

4

If we assume that the "this is a line in text" consists of one word, then we can use a macro processor:

m4 -Dword="$var" file

Where is "word" - A name is any sequence of letters, digits, and underscore, where the first character is not a digit.

nezabudka
  • 2,376
  • 5
  • 15
4

If you insert the contents of a file (not the value of a variable) with sed, it's easier:

sed -e '/^this is a line in text$/r mycert' -e '//d' file

This will parse the file called file. If a line which matches the expression ^this is a line in text$ is found, the file called mycert is inserted, without interpretation of any kind. The original line from file is then deleted.

You can get this to read from your variable like so:

printf '%s\n' "$var" |
sed -e '/^this is a line in text$/r /dev/stdin' -e '//d' file

Here, instead of reading from mycert when the line is found, sed will read from standard input, which is provided via printf. This assumes that the trigger line only occurs a single time in file.

Kusalananda
  • 320,670
  • 36
  • 633
  • 936
2

In case of the certificate, there are only two problematic characters: a slash and newline. You can use parameter expansion to replace them by \/ and \n respectivelly which sed understands:

var=${var//$'\n'/\\n/}
var=${var//\//\\\/}
sed "s/text/$var/"

But in general, I'd use Perl which knows which characters come from a variable and what characters are part of the syntax:

var=$var perl -pe 's/text/$ENV{var}/'
choroba
  • 45,735
  • 7
  • 84
  • 110