user1012135
user1012135

Reputation: 81

sed or perl regex replace section in file

I have a file, and am trying to regex to remove the the lines that has:

flannel:
    interface: $private_ipv4
    etcd_endpoints: {{ .ETCDEndpoints }}

I tried to to use this perl command, it ends up removing everything from #cloud-config till -- name: docker.service

perl -i -00ne 'print unless /(flannel:).*([^ ]+).*([^ ]+).*}}/' file.txt

file.txt:

#cloud-config
coreos:
  update:
    reboot-strategy: "off"
  flannel:
    interface: $private_ipv4
    etcd_endpoints: {{ .ETCDEndpoints }}
  etcd2:
    name: controller
    advertise-client-urls: http://$private_ipv4:2379
    initial-advertise-peer-urls: http://$private_ipv4:2380
    listen-client-urls: http://0.0.0.0:2379
    listen-peer-urls: http://0.0.0.0:2380
    initial-cluster: controller=http://$private_ipv4:2380
  units:
    - name: etcd2.service
      command: start
      runtime: true

    - name: docker.service
      drop-ins:
        - name: 40-flannel.conf
          content: |
            [Unit]
            Requires=flanneld.service
            After=flanneld.service
            [Service]
            EnvironmentFile=/etc/kubernetes/cni/docker_opts_cni.env
            ...

Upvotes: 0

Views: 463

Answers (3)

zdim
zdim

Reputation: 66883

Given the exact text you show

perl -0777 -ne 's/flannel:[^}]+}//; print' file.txt

This works by observation that there is no } inside the text to be removed. It thus uses the negated character class, [^}] which matches any character other than }. So with + it matches all up to the first }. See this in perlretut and in perlrecharclass. Please adjust for other text.

Or use

perl -0777 -ne 's/flannel:.+?(?=etcd2:)+//s; print' file.txt

which uses the positive lookahead (?=...). It is a zero width assertion, meaning that it matches up to that pattern and does not consume it. It just "looks" (asserts) that it's there. See it in perlretut. In this case we also need the modifier /s, which makes . match the newline, too.

The -0[oct/hex] switch sets the input record separator $/. The -00 sets it to an empty line thus reading by paragraphs, which isn't what you need. The -0 splits input by null character, which is very unlikely to be in a file so it normally "works" for reading the whole file at once. But a proper way to slurp a file is to specify anything greater than -0400 and -0777 is customary. See Command line switches in perlrun

Here you can use -p instead of -n and then drop print, since -p prints $_.

Upvotes: 3

Sobrique
Sobrique

Reputation: 53478

It's YAML so use a YAML parser.

#!/usr/bin/env perl
use strict;
use warnings;

use YAML::XS;
use Data::Dumper;

# <> is the magic file handle, it reads files on command line or STDIN. 
# much like grep/sed/awk do.     
my $conf = Load ( do { local $/; <> } );
#print for debugging. 
print Dumper $conf;

#delete the key you don't want. 
delete $conf->{coreos}{flannel};

#print output to STDOUT. 
#You can hack this into in place editing if you
#want. 
print Dump $conf;

Upvotes: 1

Chris Charley
Chris Charley

Reputation: 6573

In your command line spec -00ne, you are asking for 'paragraph' mode (two zeros). I think you want to slurp the file if I'm understanding your problem correctly. That would use one zero.

I used perl -0777 -pe 's/^\s+flannel:.+?(?=^\s+etcd2)//ms' file.txt to get what I think you want:

Note the flags ms. m makes the ^ match at the beginning of a 'line' instead of the default behavior of matching at the beginning of a 'string'. And the s flag allows dot (.) to also match newlines which is needed in the solution I gave.

#cloud-config
coreos:
  update:
    reboot-strategy: "off"
  etcd2:
    name: controller
    advertise-client-urls: http://$private_ipv4:2379
    initial-advertise-peer-urls: http://$private_ipv4:2380
    listen-client-urls: http://0.0.0.0:2379
    listen-peer-urls: http://0.0.0.0:2380
    initial-cluster: controller=http://$private_ipv4:2380
  units:
    - name: etcd2.service
      command: start
      runtime: true

    - name: docker.service
      drop-ins:
        - name: 40-flannel.conf
          content: |
            [Unit]
            Requires=flanneld.service
            After=flanneld.service
            [Service]
            EnvironmentFile=/etc/kubernetes/cni/docker_opts_cni.env

Upvotes: 1

Related Questions