Issue
When using GNU sed with the -s
/--separate
option, how can I skip ahead, or “fast-forward” to the end of a file or the beginning of the next? Critically, I want to do this without checking the rest of the lines against the whole script, so not just 1!d
.
s/foo/bar/ # work on some lines early in the file
[fast-forward] # ignore the rest of this file—
# *do not* run the rest of the script on it
$d # when we get to the end of this file,
# start the script over for the next file
With -s
/--separate
, addresses 1
& $
refer to the first and last lines of each file, but there are no addresses or commands specific to files themselves (except F
which doesn't help here except in debugging).
This question sounds similar, but only goes as far as discovering the -s
option. (Thanks to that Q & A for showing me how to use it!)
Solution
Short answer
:ff; $!{n;b ff}
Explanation
# Eat lines until we find the end of this file:
:ffwd # label this spot in the script
$!{ # if not at the last line ($) of *this file*
n # get the next line
b ffwd # jump (“branch”) back up to the label
}
# we only get here if we're at the last line of *a file*
d # start over at the top of the script with the next line
# (not necessary at the end of the script)
Examples
Here's an example one-liner to print only the first line containing foo
in each file:
sed -sne '/foo/{p; b ff}; d; :ff; $!{n; b ff};' *.cfg
Here's a similar invocation replacing foo
with bar
:
sed -sne 's/foo/bar/p; t ff; d; :ff; $!{n; b ff};' *.cfg
Here's a longer example (for use with sed -Esf
). It uses a similar pattern to find information that I know will be in a certain order (in a Cisco IOS config file). After the last line I care about, it formats and prints the info for that file, then fast-forwards to the end of the file.
#n # don't print by default (force -n)
# Eat lines until we find hostname
:hostname
/^hostname /!{n; b hostname}
s/^hostname (\S+)/\1/ # replace pattern with just hostname value
h # (over)write to hold space
# Eat lines until we find Loopback0
:lo0
/^interface Loopback0/!{n; b lo0}
n # grab the next line instead (should be IP)
s/^ ip address ([.[:digit:]]+) (255\.){3}255/\1/ # replace pattern with just IP
H # append to hold space
# Deal with what we found
g # get hold space contents
s/\`\s*(\S+)\s+(\S+)\s*\'/\1,\2,/ # combine lines in one comma-separated line
p # print pattern space
# Eat lines until we find the end of this file
:ffwd
$!{n; b ffwd}
# Now the script will start over with the next line,
# to wit: line 1 of the next file
Answered By - Jacktose Answer Checked By - Clifford M. (WPSolving Volunteer)