-1

I need to delete lines in a file between the specific pattern matched and the matched line.

In the below code, I want to delete the lines from object Host "kali" { to the next occurrence of } (not to the last occurrence of }). and also delete the empty spaces after deletion.

object Host "linux" {
import "windows"
address = "linux"
groups = ["linux"]
}


object Host "kali" {
import "linux"
address = "linux"
groups = [linux ]
}


object Host "windows" {
import "linux"
address = "linux"
groups = ["windows" ]
}

This is my code

clear
echo -e  "Enter the host to delete in config file"
cat > deletionfile.txt
clear
while read host
do
loc=`grep -il 'object.*Host.*"$host"' /home/afrith/config-file/*.conf`
sed -i "/^object.*Host.*\"$host\".*{$/,/^}$/d" $loc
done < deletionfile.txt
rm -rf deletionfile.txt

Error show while executing script:

sed: no input files

This is the expected output: ( when i give kali as a input to script. )

object Host "linux" {
import "windows"
address = "linux"
groups = ["linux"]
}


object Host "windows" {
import "linux"
address = "linux"
groups = ["windows" ]
}
  • 1
    Have you tried anything on your own? – oguz ismail Sep 08 '19 at 19:27
  • 1
    See also *[How to print lines between two patterns, inclusive or exclusive (in sed, AWK or Perl)?](https://stackoverflow.com/q/38972736/6136214)* Some of the answers there can, with trivial modifications, (*i.e.* removing or adding one letter), be applied to this question. – agc Sep 09 '19 at 10:34
  • Take for example [*David C. Rankin*'s answer](https://stackoverflow.com/a/38978493/6136214): `sed -n '/PAT1/,/PAT2/p' filename`, where just adding a `!`, so it becomes `sed -n '/PAT1/,/PAT2/!p' filename` answers this Q. – agc Sep 09 '19 at 10:46
  • See also: *[Using sed to delete all lines between two matching patterns](https://stackoverflow.com/q/6287755/6136214)*. Particularly [*fbicknel*'s answer](https://stackoverflow.com/a/46434705/6136214), which covers every permutation of both the positive, negative, inclusive, and exclusive variants of this question. – agc Sep 09 '19 at 10:57

5 Answers5

6

The following sed shoud remove all lines between object Host "kali" { and }.

sed '/^object Host "kali" {$/,/^}$/d'
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • Why the syntax `/,/` works only with the delete command not with substitute? –  Sep 10 '19 at 21:47
  • I used `sed 's/^object Host "kali" {$\/,\/^}$//' myfile` but it doesn't work –  Sep 10 '19 at 22:00
  • That makes no sense. What do you want to substitute for what? The `/^object Host "kali" {$/,/^}$/` is a region - all lines between pattern until pattern. You can `/^object Host "kali" {$/,/^}$/s/regex/pattern/`. – KamilCuk Sep 10 '19 at 22:11
  • it makes sense! I want to replace the matched lines with some string in my case nothing (which means delete it) –  Sep 10 '19 at 22:12
  • You can't (at least not that easy) because sed parses one line at a time. So you can't substitute multiple lines. However `sed` has some commands (it's own language actually) that are allowing to do that. Hm... I would just `/^object Host "kali" {$/,/^}$/s//replacement/` and I think it should work. – KamilCuk Sep 10 '19 at 22:13
  • So your statement is wrong (your answer work only with delete not with substitute) –  Sep 10 '19 at 22:14
  • I think even in a single line (not multiline) your answer will not work with s command. –  Sep 10 '19 at 22:17
4

Try this:

sed -i -Ez 's/object Host \"kali\"\s*\{[^}]*\}//g' your_file

Or, you could try the following (will also remove empty lines):

sed  '/object Host "kali"\s*{$/,/[^}]*}/d; /^$/d' your_file

The output will be:

object Host "linux" {
import "windows"
address = "linux"
groups = ["linux"]
}
object Host "windows" {
import "linux"
address = "linux"
groups = ["windows" ]
}
s.ouchene
  • 1,682
  • 13
  • 31
  • ` NONE='\033[00m' RED='\033[01;31m' clear echo -e "Enter the host to delete in config file" cat > deletionfile.txt clear while read hostname do loc=`grep -il 'object.*Host.*"$host"' /home/afrith/config-file/*.conf` sed -i "/^object.*Host.*\"$host\".*{$/,/^}$/d" $loc done < deletionfile.txt rm -rf deletionfile.txt – mohamed afrid Sep 10 '19 at 12:29
1

One of the way of doing it will be using AWK. You need to change the record separator in to } and run this awk '!/kali/;BEGIN{RS="}"; ORS="}"}' < your_file , the output is what you are looking for.

I have saved the text given in the question in file - test.txt

ajay@DESKTOP-PCMKVK6:~$ cat test.txt
object Host "linux" {
import "windows"
address = "linux"
groups = ["linux"]
}


object Host "kali" {
import "linux"
address = "linux"
groups = [linux ]
}


object Host "windows" {
import "linux"
address = "linux"
groups = ["windows" ]
}

now we need to run this awk command awk '!/kali/;BEGIN{RS="}"; ORS="}"}' < test.txt

ajay@DESKTOP-PCMKVK6:~$ awk '!/kali/;BEGIN{RS="}"; ORS="}"}' < test.txt
object Host "linux" {
import "windows"
address = "linux"
groups = ["linux"]
}


object Host "windows" {
import "linux"
address = "linux"
groups = ["windows" ]
}
}ajay@DESKTOP-PCMKVK6:~$ 

Also, you can use sed as mentioned by other answer and you can add -i option to make that change in the same file sed -i '/^object Host "kali" {$/,/^}$/d' file_name

Ajay
  • 775
  • 5
  • 19
1

You can make gnu awk to work in block mode by setting record selector to nothing.

awk -v RS= -v ORS="\n\n" '/object Host "kali"/ {next} 1' file
object Host "linux" {
import "windows"
address = "linux"
groups = ["linux"]
}

object Host "windows" {
import "linux"
address = "linux"
groups = ["windows" ]
}
  • /object Host "kali"/ {next} if block contains object Host "kali", skip it
  • 1 always true, do the default action, print
Jotne
  • 40,548
  • 12
  • 51
  • 55
1

This might work for you (GNU sed):

sed -n '/^object Host "kali" {/{:a;n;/^}/d;ba};p' file

Or:

sed '/^object Host "kali" {/{:a;N;/^}/Md;ba}' file

The first solution acts like grep and chooses not to print the lines starting with object Host "kali" { and ending with a line starting }.

The second solution gathers up the lines between the above two patterns and then deletes the lines.

The first solution will alter the file if the first pattern matches whereas the second solution only alters the file if both pattern matches. This is because the commands n and N cause sed to end processing if they are asked to read beyond the end of the file. n will end quietly if the -n option is set whereas N will print what ever is in the pattern space before ceasing.

N.B. /^}/ matches a } at the start of line, in the second solution the M flag is added which turns on the multiline matching i.e. the ^ and $ match after/before a newline this flag is specific to GNU sed. /\n}/ will have the same effect. If the match is more relaxed i.e. to match any occurrence of },/}/ is suffice.

potong
  • 55,640
  • 6
  • 51
  • 83