regexp..prepending a line globally (Vi)

213 views
Skip to first unread message

Daniel Smith - OPD Gang

unread,
Jul 1, 1990, 4:00:46 PM7/1/90
to

All my years in vi and this one stumps me! I wanted
to make a real quick shell script, taken from the output
of an ls-lR on uunet. So, I get these lines:

/usr/spool/ftp/comp.sources.unix/volume22/nn6.4:
part01.Z
part02.Z
part03.Z
part04.Z
.
.
. etc...

So far so good. Now what I wanted to do is take the first
line (with the path) and prepend it to every line with a regexp. I know
how to do this quickly by hand and all that, but thought I should
be able to do this easily with something like:

:g/\(.*spool.*\)/s/^part/\1\/part/g

which is wrong. What I want to end up with is:

/usr/spool/ftp/comp.sources.unix/volume22/nn6.4/part01.Z
/usr/spool/ftp/comp.sources.unix/volume22/nn6.4/part02.Z

I'll do it by hand this time, but I'd really like to know
"how to capture a pattern on a given line, and then apply it on
other lines that match another pattern". Thanks for any help.

Oh, and before we get into editor wars...I am trying to learn
GNU Emacs...it's just hard to make the shift after 8 years of vi ;-)

Daniel
--
dans...@well.sf.ca.us dan...@island.uu.net unicom!dan...@pacbell.com
ph: (415) 332 3278 (h), 491 1000 (w) disclaimer: Island's coffee was laced :-)

Maarten Litmaath

unread,
Jul 2, 1990, 11:12:50 AM7/2/90
to
In article <17...@island.uu.net>,
dan...@island.uu.net (Daniel Smith - OPD Gang) writes:
)...
)/usr/spool/ftp/comp.sources.unix/volume22/nn6.4:
)part01.Z
)part02.Z
)part03.Z
)part04.Z
).
).
). etc...
)
) So far so good. Now what I wanted to do is take the first
)line (with the path) and prepend it to every line with a regexp. [...]

Not so trivial, I think. The following sh/sed solution works.
Try to figure it out!
--------------------cut here--------------------
SED='
/spool/{
: dir
s/:$/\//
h
: file
n
/spool/b dir
H
g
s/\n//
p
x
s/\n.*//
x
b file
}
p
'

sed -n "$SED" ls-output-file
--
"and with a sudden plop it lands on usenet. what is it? omigosh, it must[...]
be a new user! quick kill it before it multiplies!" (Loren J. Miller)

Randal Schwartz

unread,
Jul 2, 1990, 12:49:49 PM7/2/90
to
In article <17...@island.uu.net>, daniel@island (Daniel Smith - OPD Gang) writes:
|
| All my years in vi and this one stumps me! I wanted
| to make a real quick shell script, taken from the output
| of an ls-lR on uunet. So, I get these lines:
|
| /usr/spool/ftp/comp.sources.unix/volume22/nn6.4:
| part01.Z
| part02.Z
| part03.Z
| part04.Z
| .
| .
| . etc...
|
| So far so good. Now what I wanted to do is take the first
| line (with the path) and prepend it to every line with a regexp.

You can't do it in vi without resorting to some macro trickery. (Of
course, the vi hackers will now come out of the woodwork to prove me
wrong... after all, this *is* Usenet. :-)

In Perl, it'd be:

perl -ne 'if (/^(.*):$/) { $pre = $1; } else { print "$pre/$_"; }'

Just another Perl hacker,
--
/=Randal L. Schwartz, Stonehenge Consulting Services (503)777-0095 ==========\
| on contract to Intel's iWarp project, Beaverton, Oregon, USA, Sol III |
| mer...@iwarp.intel.com ...!any-MX-mailer-like-uunet!iwarp.intel.com!merlyn |
\=Cute Quote: "Welcome to Portland, Oregon, home of the California Raisins!"=/

Larry Wall

unread,
Jul 2, 1990, 2:47:23 PM7/2/90
to
In article <70...@star.cs.vu.nl> ma...@cs.vu.nl (Maarten Litmaath) writes:
: In article <17...@island.uu.net>,

Which just goes to show ya...

Real programmers can write assembly code in any language. :-) :-) :-)

For those who believe in the waterbed theory of language complexity,
how 'bout:

#!/usr/bin/perl -p
if (s#^(/.*):\n##)
{ $prefix = $1; }
else
{ s#^#$prefix/#; }

Fairly trivial, I think.

Larry Wall
lw...@jpl-devvax.jpl.nasa.gov

P.S. It's not your fault, Maarten--if you push a waterbed down in one spot,
it just naturally goes up somewhere else.

Eli Taub/100000

unread,
Jul 2, 1990, 4:23:20 PM7/2/90
to
In article <17...@island.uu.net> dan...@island.uu.net (Daniel Smith - OPD Gang) writes:
>
> All my years in vi and this one stumps me! I wanted
>to make a real quick shell script, taken from the output
>of an ls-lR on uunet. So, I get these lines:
>
>/usr/spool/ftp/comp.sources.unix/volume22/nn6.4:
>part01.Z
>part02.Z
>.
>. etc...
>
Comments deleted ...

>
>What I want to end up with is:
>/usr/spool/ftp/comp.sources.unix/volume22/nn6.4/part01.Z
>/usr/spool/ftp/comp.sources.unix/volume22/nn6.4/part02.Z
>
> Daniel
>--
> dans...@well.sf.ca.us dan...@island.uu.net unicom!dan...@pacbell.com
>ph: (415) 332 3278 (h), 491 1000 (w) disclaimer: Island's coffee was laced :-)

The real answer should be - use sed / awk ...,
or use `find <dir> -print' when possible.
But since you asked for it ...:

$s/$/^M:/
g/^.*:$/s @^\(.*\):$@.,/:/-1 s!^!\1/!@ \
d a \
*a

[ ^M stands for: <CONTROL>V <ENTER> ]
To use, simply insert the above lines into a file (e.g. exls),
and source it like so (with `so') in ex mode: :so exls

Explanation:

$s/$/^M:/
^^^^^^^^^
Replace last EOL with NL and `:' (add extra line at end of file with `:'),
explained why this is needed later.

g/^.*:$/s @^\(.*\):$@.,/:/-1 s!^!\1/!@ \
^^^^^^^^
For each line ending with `:' do the following:

1. g/^.*:$/s @^\(.*\):$@.,/:/-1 s!^!\1/!@ \
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
While saving everything on the line but `:', substitute it with:
.,/:/-1 s!^!\1/!
where `\1' it replaced with the saved regexp.
For example a line looking like so `/usr/bin:' would become:
.,/:/-1 s!^!/usr/bin/!

g/^.*:$/s @^\(.*\):$@.,/:/-1 s!^!\1/!@ \
^^^^^^^
On every line from current line (found by the `g' command above),
to the line before the next line having a `:'.
(In order for search to succeed on last directory we need to add
a line containing `:' - see first command above.)

g/^.*:$/s @^\(.*\):$@.,/:/-1 s!^!\1/!@ \
^^^^^^^^
Substitute start of line (i.e. add to front of line) the regexp saved.

g/^.*:$/s @^\(.*\):$@.,/:/-1 s!^!\1/!@ \
^
The next line is a subcommand, to ran in same context as this command.

2. d a \
^^^
Delete the line created above and put it into the named buffer `a'.

3. *a
^^
Execute the command found in the named buffer `a' as if it were typed in.
This will do the substitute that was deleted into the named buffer.

Comment: I use `@' and `!' for regexp delimiter so that `/' in pathnames will
not mess up the `s' commands.

_ |___
Eli Taub | Who needs emacs when you have vi | | | \ |
(512) 838-4810 | | | /\/
Contractor at (AWD) IBM | I express my opinions not IBM's. | / | \

Leo de Wit

unread,
Jul 3, 1990, 11:11:30 AM7/3/90
to
In article <85...@jpl-devvax.JPL.NASA.GOV> lw...@jpl-devvax.JPL.NASA.GOV (Larry Wall) writes:
|In article <70...@star.cs.vu.nl> ma...@cs.vu.nl (Maarten Litmaath) writes:
|:
|: Not so trivial, I think. The following sh/sed solution works.
|: Try to figure it out!
[about 18 lines of sed script omitted]

|
|Which just goes to show ya...
|
| Real programmers can write assembly code in any language. :-) :-) :-)
|
|For those who believe in the waterbed theory of language complexity,
|how 'bout:
|
| #!/usr/bin/perl -p
| if (s#^(/.*):\n##)
| { $prefix = $1; }
| else
| { s#^#$prefix/#; }
|
|Fairly trivial, I think.

OK, Larry, you asked for it :-)

sed '/spool/h
//d
G
s/\(.*\)\n\(.*\):/\2\/\1/' $*

Trivial, indeed.

Leo.

Larry Wall

unread,
Jul 3, 1990, 4:42:21 PM7/3/90
to
In article <8...@ehviea.ine.philips.nl> l...@ehviea.UUCP (Leo de Wit) writes:
: In article <85...@jpl-devvax.JPL.NASA.GOV> lw...@jpl-devvax.JPL.NASA.GOV (Larry Wall) writes:
: | #!/usr/bin/perl -p

: | if (s#^(/.*):\n##)
: | { $prefix = $1; }
: | else
: | { s#^#$prefix/#; }
: |
: |Fairly trivial, I think.
:
: OK, Larry, you asked for it :-)
:
: sed '/spool/h
: //d
: G
: s/\(.*\)\n\(.*\):/\2\/\1/' $*
:
: Trivial, indeed.

That's fine if you're not dyslexic about slashes and backslashes.

If you're gunnin' for the shortest command, try this one on for size:

perl -ne '/:/||print"$`/$_"' $*

(I'll freely admit that if the contest were to double space a file, sed
would win easily.)

Larry

John Macdonald

unread,
Jul 3, 1990, 11:34:05 AM7/3/90
to
In article <17...@island.uu.net> dan...@island.uu.net (Daniel Smith - OPD Gang) writes:
|
| All my years in vi and this one stumps me! I wanted
|to make a real quick shell script, taken from the output
|of an ls-lR on uunet. So, I get these lines:
|
|/usr/spool/ftp/comp.sources.unix/volume22/nn6.4:
|part01.Z
|part02.Z
|part03.Z
|part04.Z
|.
|.
|. etc...
|
| So far so good. Now what I wanted to do is take the first
|line (with the path) and prepend it to every line with a regexp.

Assuming that your implication that there is only one of these
series present (i.e. only line 1 has a path), an answer is to
use the ex side of vi (use Q if you are in vi mode to get to
ex mode, or else precede each line with a colon):

3,$g/^/1co.-1 (duplicate the path line in front of all files)
g/:$/join (join each pair)
%s;: ;/; (change the : to a slash)

However, if you have more than one of the path lines mixed in, then
this requires much more contorted effort to do - it is easier to use
recursive invokations of this technique than to try to get it to work
all at once...

(again from ex - assuming that the above script is in file "joinup"):

$a (ensure that there is always one more colon)
:
.
g/:/.,/:/-1! ex <joinup (for each path range, run the previous script)
$d (get rid of the extra colon line)

(In fact, I would probably write a Perl script to do this instead, except
that Randall has beaten me to it...)
--
Algol 60 was an improvment on most | John Macdonald
of its successors - C.A.R. Hoare | jmm@eci386

Leslie Mikesell

unread,
Jul 3, 1990, 11:59:41 AM7/3/90
to
In article <32...@d75.UUCP> e...@reed.UUCP (Eli Taub/100000) writes:

[how to do this in vi]:
>>/usr/spool/ftp/comp.sources.unix/volume22/nn6.4:
>>part01.Z
>>part02.Z
>Comments deleted ...

>>What I want to end up with is:
>>/usr/spool/ftp/comp.sources.unix/volume22/nn6.4/part01.Z
>>/usr/spool/ftp/comp.sources.unix/volume22/nn6.4/part02.Z

>....


>But since you asked for it ...:
>$s/$/^M:/
>g/^.*:$/s @^\(.*\):$@.,/:/-1 s!^!\1/!@ \
>d a \
>*a

Good grief!
Just edit the first line into the command for the transformation, delete
it into a register, and execute the register. This gives you a series
of steps you can undo if you mis-type something.

edit to:
:%s,^,/usr/spool/ftp/comp.sources.unix/volume22/nn6.4/,
with the cursor on that line, type "add (delete to register a)
then @a (execute register a)
if it doesn't look right, just u (undo) "aP (put back from register a)
and try again. The only problem to watch for with this approach is
that you need an extra level of ^V quoting for any special characters
(one to get them into the edit buffer, one to get them into the command).

Les Mikesell
l...@chinet.chi.il.us

Eli Taub/100000

unread,
Jul 6, 1990, 12:58:00 PM7/6/90
to

You are quite correct if you only had one directory to prepend to ALL
the lines, but what do you do with the output of say `ls -R /usr' ?
The above code will automaticaly find the correct directory to prepend
to each line.
For example on the file:

/usr:
bin
etc
lib
/usr/etc:
rup
spray
/usr/include:
core.h
curses.h
errno.h

If you source the code above you'll get:
/usr/bin
/usr/etc
/usr/lib
/usr/etc/rup
/usr/etc/spray
/usr/include/core.h
/usr/include/curses.h
/usr/include/errno.h

And you can undo the whole thing with 1 `u'!!

_ |___
Eli Taub | | \ |

Reply all
Reply to author
Forward
0 new messages