LinuxQuestions.org
Share your knowledge at the LQ Wiki.
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 03-25-2011, 06:56 AM   #1
jacky29
LQ Newbie
 
Registered: Mar 2011
Posts: 14

Rep: Reputation: 0
deleting lines above and below a word!


hi...
i jst want to delete a host entry from httpd.conf
for eg:


i have entries such as:

<VirtualHost 192.168.1.157:80>
DocumentRoot /home/karthik
ServerName www.karthik.com
</VirtualHost>
<VirtualHost 192.168.1.157:80>
DocumentRoot /home/karthik1
ServerName www.karthik1.com
</VirtualHost>
<VirtualHost 192.168.1.157:80>
DocumentRoot /home/lalz
ServerName lal.com
</VirtualHost>

i want to delete the entry containing the domain name lal.com
ie..the output should be:
<VirtualHost 192.168.1.157:80>
DocumentRoot /home/karthik
ServerName www.karthik.com
</VirtualHost>
<VirtualHost 192.168.1.157:80>
DocumentRoot /home/karthik1
ServerName www.karthik1.com
</VirtualHost>



the entry for lal.com should be deleted!!
anyone..plz help me out!!
 
Old 03-25-2011, 07:03 AM   #2
repo
LQ 5k Club
 
Registered: May 2001
Location: Belgium
Distribution: Arch
Posts: 8,528

Rep: Reputation: 899Reputation: 899Reputation: 899Reputation: 899Reputation: 899Reputation: 899Reputation: 899
You could use
Code:
sed -i '/lal.com/d' httpd.conf
Make a backup of the file before proceeding.

Kind regards
 
Old 03-25-2011, 07:07 AM   #3
jacky29
LQ Newbie
 
Registered: Mar 2011
Posts: 14

Original Poster
Rep: Reputation: 0
thankz for the reply..bt the command doesnt work!!



sed '/lal/d' httpd.conf


only deletes that particular line...
plz help me out!!
 
Old 03-25-2011, 08:12 AM   #4
druuna
LQ Veteran
 
Registered: Sep 2003
Posts: 10,532
Blog Entries: 7

Rep: Reputation: 2392Reputation: 2392Reputation: 2392Reputation: 2392Reputation: 2392Reputation: 2392Reputation: 2392Reputation: 2392Reputation: 2392Reputation: 2392Reputation: 2392
Hi,

Using sed:

Create a file with the following content:
Code:
:t
/<VirtualHost/,/VirtualHost>/ { # For each line in this range
  /VirtualHost>/!{    # If not at the /VirtualHost>/ marker
    $!{               # nor the last line of the file,
      N;              # add the Next line to the pattern space
      bt
    }                 # and branch (loop back) to the :t label.
  }                   # This line matches the /VirtualHost>/ marker.
  /lal.com/d;         # If /lal.com/ matches, delete the block.
}                     # Otherwise, the block will be printed.
Save file (this example uses sedcmds).

Run sed as follows:
Code:
sed -f sedcmds infile
Example run:
Code:
$ cat infile
<VirtualHost 192.168.1.157:80>
DocumentRoot /home/karthik
ServerName www.karthik.com
</VirtualHost>
<VirtualHost 192.168.1.157:80>
DocumentRoot /home/karthik1
ServerName www.karthik1.com
</VirtualHost>
<VirtualHost 192.168.1.157:80>
DocumentRoot /home/lalz
ServerName lal.com
</VirtualHost>

$ sed -f sedcmds infile
<VirtualHost 192.168.1.157:80>
DocumentRoot /home/karthik
ServerName www.karthik.com
</VirtualHost>
<VirtualHost 192.168.1.157:80>
DocumentRoot /home/karthik1
ServerName www.karthik1.com
</VirtualHost>
Hope this helps.

EDIT:
Or as a one-liner:
Code:
sed ':t /<VirtualHost/,/VirtualHost>/ { /VirtualHost>/!{ $!{ N; bt } }; /lal.com/d; }' infile

Last edited by druuna; 03-25-2011 at 08:40 AM. Reason: Added one-liner example
 
1 members found this post helpful.
Old 03-25-2011, 08:19 AM   #5
Nominal Animal
Senior Member
 
Registered: Dec 2010
Location: Finland
Distribution: Xubuntu, CentOS, LFS
Posts: 1,723
Blog Entries: 3

Rep: Reputation: 947Reputation: 947Reputation: 947Reputation: 947Reputation: 947Reputation: 947Reputation: 947Reputation: 947
Rather than having all Apache virtual hosts configured in a single file, I recommend using instead
Code:
    Include *.vhost
and creating a separate file for each virtual host (in the base Apache configuration file directory, named domainname.vhost). That way you could just delete the file.

However, your current problem can be easily solved via awk. It is not very elegant, but it should work well for you. It is not terribly well tested, but it worked flawlessly with the few VirtualHost configurations I tried it with. It modifies the specified configuration file, but saves the previous version with the suffix .saved.
Code:
#!/bin/bash

# Usage?
if [ $# -lt 2 ] || [ "$1" == "-h" ] || [ "$1" == "--help" ]; then
    echo "" >&2
    echo "Usage: $0 [ -h | --help ]" >&2
    echo "       $0 configfile servername ..." >&2
    echo "" >&2
    echo "This will remove virtual servers containing servername(s)" >&2
    echo "from the specified configfile." >&2
    echo "" >&2
    exit 0
fi

FILE="$1"
if [ ! -f "$FILE" ]; then
    echo "$FILE: No such file." >&2
    exit 1
fi
shift 1

# Create an automatically removed temporary work directory $WORK
WORK="`mktemp -d`" || exit $?
trap 'rm -rf "$WORK"' EXIT

# awk script to omit VirtualHosts with matching ServerNames
if ! awk -v "namelist=$*" '
    BEGIN {
        RS="[\n\r]"
        IFS="[\t\v\f ]+"

        # Parse namelist into name array using whitespace separators.
        namelist = tolower(namelist)
        gsub(/[\t\n\v\f\r ]+/, " ", namelist)
        sub(/^ /, "", namelist)
        sub(/ $/, "", namelist)
        split(namelist, name, " ")

        # Initial mode.
        line = 0
        skip = 0
    }

    (tolower($1) == "<virtualhost") {
        split("", vhost)
        skip = 0
        line = 1
        vhost[line] = $0
        next
    }

    {
        if (line)
            vhost[++line] = $0
        else
            print
    }

    (tolower($1) == "servername") {
        value = tolower($2)
        for (n in name)
            if (index(value, name[n]) > 0)
                skip = 1;
    }

    (tolower($1) == "</virtualhost>") {
        if (!skip)
            for (n = 1; n <= line; n++)
                print vhost[n]

        skip = 0
        line = 0
        next
    }
' "$FILE" > "$WORK/output" ; then
    echo "$FILE: Error processing file, aborted." >&2
    exit 1
fi

# Successful. Replace original file with the work file.
# But first save a backup copy.
mv -f "$FILE" "$FILE.saved"
mv -f "$WORK/output" "$FILE"
The shell script prints the usage information if necessary, creates (and autodeletes) a safe temporary working directory, saves and replaces the original file if success, or prints an error message if necessary. The real work is done in the awk script.

The awk script is a case insensitive line-based state machine. In the default state, it just outputs each input line as is.

When the awk script encounters a line with the first token matching <VirtualHost, it changes state, and starts saving each input line into (a new) array vhost instead of outputting them.

If the awk script encounters an input line with the first token matching ServerName, it checks if any of the specified server names are contained in the second token. If yes, this array is marked to be skipped.

When the script encounters a line with the first token matching </VirtualHost>, it will reset to default mode. If the array was not marked skipped, all lines in the array are output at this point.

There are a few minor tricks in there to make sure the script works with both gawk (GNU awk, my favourite) and mawk, but they're not very important unless you're very interested in awk scripting.

Hope you find this useful.
 
Old 03-25-2011, 08:24 AM   #6
crts
Senior Member
 
Registered: Jan 2010
Posts: 1,665

Rep: Reputation: 492Reputation: 492Reputation: 492Reputation: 492Reputation: 492
Hi,

this worked with your sample data
Code:
sed ':a N; \@</VirtualHost>@ {/lal.com/ d;b}; ba' file
 
1 members found this post helpful.
Old 03-25-2011, 08:30 AM   #7
kurumi
Member
 
Registered: Apr 2010
Posts: 228

Rep: Reputation: 46
Code:
$ awk 'BEGIN{ ORS=RS="</VirtualHost>"} /lal\.com/{next} RT{print $0} '  file

or Ruby(1.9+)
Code:
$ ruby -0777 -ne '$_.split(/<\/VirtualHost>\n/).each{|x| puts x+"</VirtualHost>" if not x[/lal\.com/]  }' file

Last edited by kurumi; 03-25-2011 at 08:33 AM.
 
Old 03-25-2011, 08:32 AM   #8
carltm
Member
 
Registered: Jan 2007
Location: Canton, MI
Distribution: CentOS, SuSE, Red Hat, Debian, etc.
Posts: 703

Rep: Reputation: 97
Hi crts,

I've done a lot with sed, but I don't recognize what this command does.
Would you please explain the syntax?
 
Old 03-25-2011, 08:52 AM   #9
crts
Senior Member
 
Registered: Jan 2010
Posts: 1,665

Rep: Reputation: 492Reputation: 492Reputation: 492Reputation: 492Reputation: 492
Quote:
Originally Posted by carltm View Post
Hi crts,

I've done a lot with sed, but I don't recognize what this command does.
Would you please explain the syntax?
Hi,

I assume you do not recognize the non-standard delimiter in '\@</VirtualHost>@', right? This does the same as
Code:
'/<\/VirtualHost>/'
The backslash before the @ simply indicates that @ shall be used as delimiter instead of the standard '/'.
 
1 members found this post helpful.
Old 03-25-2011, 08:57 AM   #10
druuna
LQ Veteran
 
Registered: Sep 2003
Posts: 10,532
Blog Entries: 7

Rep: Reputation: 2392Reputation: 2392Reputation: 2392Reputation: 2392Reputation: 2392Reputation: 2392Reputation: 2392Reputation: 2392Reputation: 2392Reputation: 2392Reputation: 2392
@crts: Nice short solution!!

BTW, the </ part isn't needed for the sample data, so this would also work:
Code:
sed ':a N; /VirtualHost>/ {/lal.com/ d;b}; ba' file
 
Old 03-25-2011, 09:03 AM   #11
carltm
Member
 
Registered: Jan 2007
Location: Canton, MI
Distribution: CentOS, SuSE, Red Hat, Debian, etc.
Posts: 703

Rep: Reputation: 97
Yes, that's the first I've seen that the delimiter.

Also I'm guessing that the ":a N;" and "; ba" set up a loop in
which the "{...}" command is run. Does it simply match from the
first occurrence of "\@</VirtualHost>@" to the next occurrence?

Or does it effectively create sections, and if "/lal.com/" is
matched it deleted the section?
 
Old 03-25-2011, 09:15 AM   #12
crts
Senior Member
 
Registered: Jan 2010
Posts: 1,665

Rep: Reputation: 492Reputation: 492Reputation: 492Reputation: 492Reputation: 492
Hi druuna,

you are right. I just had a look at your solution and noticed that you omit the '</' part. This spares us the obfuscating escape character.
Examining your solution a bit further, you can transform it to
Code:
sed ':t /VirtualHost>/! {N;bt}; /lal.com/ d' infile
which has "negative" check conditions to loop the file. It is also shorter than mine by one instruction.

Last edited by crts; 03-25-2011 at 09:23 AM.
 
Old 03-25-2011, 09:22 AM   #13
crts
Senior Member
 
Registered: Jan 2010
Posts: 1,665

Rep: Reputation: 492Reputation: 492Reputation: 492Reputation: 492Reputation: 492
Quote:
Originally Posted by carltm View Post
Yes, that's the first I've seen that the delimiter.

Also I'm guessing that the ":a N;" and "; ba" set up a loop in
which the "{...}" command is run. Does it simply match from the
first occurrence of "\@</VirtualHost>@" to the next occurrence?

Or does it effectively create sections, and if "/lal.com/" is
matched it deleted the section?
It does the latter. I reads every line into the pattern buffer. If '\@</VirtualHost>@' is encountered it checks for /lal.com/ and deletes the pattern buffer if it finds lal.com. Regardless of the presence of lal.com it then jumps to the end of the sed script which also triggers sed's default action to print the pattern buffer. The buffer will be empty if lal.com were previously encountered; otherwise the content is printed and the script starts again with processing the next block.
 
1 members found this post helpful.
Old 03-25-2011, 09:53 AM   #14
grail
LQ Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 9,629

Rep: Reputation: 2944Reputation: 2944Reputation: 2944Reputation: 2944Reputation: 2944Reputation: 2944Reputation: 2944Reputation: 2944Reputation: 2944Reputation: 2944Reputation: 2944
More awk but kinda the same:
Code:
awk 'BEGIN{ORS=RS="</VirtualHost>\n"}!/lal\.com/ && RT' file
 
Old 03-25-2011, 10:31 AM   #15
crts
Senior Member
 
Registered: Jan 2010
Posts: 1,665

Rep: Reputation: 492Reputation: 492Reputation: 492Reputation: 492Reputation: 492
Quote:
Originally Posted by grail View Post
More awk but kinda the same:
Code:
awk 'BEGIN{ORS=RS="</VirtualHost>\n"}!/lal\.com/ && RT' file
Shouldn't this be 'RS' instead of 'RT'?
Also, if the file has an empty line at the end I get a duplicate output of '</VirtualHost>' in the last line.
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
deleting duplicate lines without deleting first instance of the duplicated line jkeertir Linux - Newbie 2 02-07-2011 07:55 AM
deleting a line containing the word mohamad Linux - Newbie 6 06-10-2010 05:20 PM
print second word in 1st line along with 5th word in all the lines after the first bangaram Programming 5 08-31-2009 04:42 AM
Deleting lines from a file horacioemilio Programming 16 12-18-2007 07:07 PM
deleting files containing a specific word Sleb Programming 5 05-25-2006 02:19 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 07:23 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration