Bitcoin Barron
Bitcoin Barron

Reputation: 15

Using multiple sed commands

Hi I'm looking to search through a file and output the values of a line that matches the following regex with the matching text removed, I don't need it output to a file. This is what I am currently using and it is outputting the required text but multiple times:

#!/bin/sh

for file in *; do
sed -e 's/^owner //g;p;!d ; s/^admin //g;p;!d ; s/^loc //g;p;!d ; s/^ser //g;p;!d' $file
done

The preferred format would be something like this so I could have control over what happens inbetween:

for file in *; do
    sed 's/^owner //g;p' $file | head -1
    sed 's/^admin //g;p' $file | head -1
    sed '/^loc //g;p' $file | head -1
    sed '/^ser //g;p' $file | head -1
done

An example input file would be the following:

owner sys group
admin guy
loc Q-30934
ser 18r9723
comment noisy fan is something

and the required output is the following:

sys group
guy
Q-30934
18r9723

Upvotes: 0

Views: 693

Answers (3)

potong
potong

Reputation: 58508

This might work for (GNU sed):

sed '0,/^owner /{//s///p};0,/^admin /{//s///p};0,/^loc /{//s///p};0,/^ser /{//s///p}' file

Creates a series of toggle switches, one for each of the desired strings. The switches apply once only throughout the file for each string i.e. only the first occurence of each string is printed.

An alternative and depending on file sizes maybe quicker method:

sed -rn '1{x;s/^/owner admin loc ser /;x};/^(owner |admin |loc |ser )/{G;/^(owner |admin |loc |ser )(.*\n.*)\1/!b;s//\2/;P;/\n$/q;s/.*\n//;h}' file

This preps the hold space with the desired strings. For only those lines that contain the desired strings, append the hold space and check if the current line needs to be amended. Match the desired string with the same string in the hold space. If the line has already appeared the match will fail and the line can be disregarded. If the line is yet to be amended, the desired string is removed from the current line and then the first half of the line is printed. If no strings appear in the remaining half of the line the process is over and can be quit. Otherwise remove the first half of the string and replace the hold space with the desired string removed.

Upvotes: 1

Jonathan Leffler
Jonathan Leffler

Reputation: 754760

I'd suggest:

sed -n -e '/^owner / { s///; p; }' \
       -e '/^admin / { s///; p; }' \
       -e '/^loc /   { s///; p; }' \
       -e '/^ser /   { s///; p; }' \
    *

sed is perfectly capable of reading many files, so the loop control is unnecessary (you aren't doing per-file I/O redirection, for example) and it's reasonable to list the files after the rest of the sed command (that's the * on its own). If you've got a more modern version of sed (e.g. GNU sed), you can combine the patterns into a single line:

sed -r -n -e '/^(owner|admin|loc|ser) / { s///; p; }' *

Upvotes: 1

Peter Westlake
Peter Westlake

Reputation: 5036

You're giving sed the p (for Print) command several times. It prints the entire line each time. And unless you tell it not to with the -n option, sed will print the line at the end anyway.

You also give the !d command multiple times.

Edited after you added the multiple-sed version: instead of using head -q, just use -n to avoid printing lines you don't want. Or even use q (Quit) to stop processing after printing the bit you do want.

For instance:

sed -n '/^owner / { s///gp; q; }' $file

The {} group the substitution and quit commands together, so that they are both executed if and only if the pattern is matched. Having used the pattern in the address at the beginning, you can leave it out of the s command. So that command is short for:

sed -n '/^owner / { s/^owner //gp; q; }' $file

Upvotes: 2

Related Questions