[sf-lug] vi/ex: %, #, !, and shell file globbing in ex command arguments

Michael Paoli Michael.Paoli at cal.berkeley.edu
Mon Nov 16 19:04:11 PST 2020


> From: "Rick Moen" <rick at linuxmafia.com>
> Subject: Re: [sf-lug] entering dates in vim text editor
> Date: Mon, 16 Nov 2020 15:21:47 -0800

> :r! date +%c
> /tmp/mutt-linuxmafia-1000-23914-1114c
>
> :r! date +'\%c'
> Mon 16 Nov 2020 03:18:13 PM PST
>
> Wow, pain in the tochis, huh?  Some days, I really hate having to always
> worry about weird little shell quoting and escaping problems.

No, not shell, a highly useful feature of ex ... that's been around
(nearly?) forever, and is also codified in POSIX.

E.g.:
$ cd "$(mktemp -d)"
$ >./"$(openssl rand -base64 20 | sed -e 's/[^a-zA-Z0-9]//g')"
$ >./"$(openssl rand -base64 20 | sed -e 's/[^a-zA-Z0-9]//g')"
$ echo *
32eyxG6upe9cEXVusggpxzFmp9M MQSDvZtaN6wEj7yr8dQ8VYu0qU
$ echo original content > 32eyxG6upe9cEXVusggpxzFmp9M
$ ex *
32eyxG6upe9cEXVusggpxzFmp9M: 2 files to edit: unmodified: line 1
:$a
Let's say I add some text to this file, and I'm editing along ...
Then I think, oh my gosh, I didn't save the original, I don't want to
clobber it.  Well, no need to get out of ex/vi.  Let's say I want to
save a copy of the original, with same name, but with .BAK appended.
Okay, easy enough ... even with filename that sucks ...
Haven't written out my changes (yet), so ... actually instead,
let's say I want to move/rename the original to having a .BAK extension,
and write what I'm editing to a reasonable_filename.  Again, no need
to leave vi/ex.
.
:w !cat > reasonable_filename
!
:!mv % %.BAK; ls
!m
File modified since last write.
32eyxG6upe9cEXVusggpxzFmp9M.BAK  reasonable_filename
MQSDvZtaN6wEj7yr8dQ8VYu0qU
!
:!echo now let\'s change files I\'m working on, again no need to leave vi/ex
File modified since last write.
now let's change files I'm working on, again no need to leave vi/ex
!
:n! r* M*
reasonable_filename: 2 files to edit: unmodified: line 10
:$a
Now let's add some more notes.
I intentionally did:
w !cat > reasonable_filename
rather than:
w! reasonable_filename
at least at that point, as I didn't (yet) want vi/ex to "remember"
the new filename reasonable_filename, so I could still use % for the
shortcut to (expansion of) the filename I/vi/ex was working on (and
likewise # if I have/had an alternate filename).  So, where were we,
ah, appending notes.  I also did the
n! r* M*
to start editing a new set of files - here vi/ex (not shell) does
shell-type metasyntax expansion/matching of filenames for us.  I can see
the files with arg:
.
:arg
[reasonable_filename] MQSDvZtaN6wEj7yr8dQ8VYu0qU
:$a
Let's write this out and go on to next.
:w
:n
Oops, forgot to quit append.
.
:$-2,$d
Let's write this out and go on to next.
:w
reasonable_filename: 25 lines, 1203 characters
:n
MQSDvZtaN6wEj7yr8dQ8VYu0qU: unmodified: line 1
:r !echo 'this filename (%) sucks!'
!e
this filename (MQSDvZtaN6wEj7yr8dQ8VYu0qU) sucksls
:d
:r !echo 'this filename (%) sucks\!'
!e
this filename (MQSDvZtaN6wEj7yr8dQ8VYu0qU) sucks!
:w
MQSDvZtaN6wEj7yr8dQ8VYu0qU: 1 lines, 50 characters
:!echo now let us go to our alternate file - that would be the one we  
were immediately previously editing - which vi/ex remembers ...
now let us go to our alternate file - that would be the one we were  
immediately previously editing - which vi/ex remembers ...
!
:e#
reasonable_filename: unmodified: line 25
:!echo let us make a duplicate copy - could do that via operating  
system, or have vi/ex handle that for us - we will do the latter this  
time.
let us make a duplicate copy - could do that via operating system, or  
have vi/ex handle that for us - we will do the latter this time.
!
:w! %.duplicate
reasonable_filename.duplicate: new file: 25 lines, 1203 characters
:arg
reasonable_filename [MQSDvZtaN6wEj7yr8dQ8VYu0qU]
:!echo we can flip back and forth on alternate
we can flip back and forth on alternate
!
:e#
reasonable_filename.duplicate: unmodified: line 25
:e#
reasonable_filename: unmodified: line 25
:!echo note how since we used w\! file rather than w \! cat \> file,  
vi/ex now has switched to using that name.  Okay, enough mucking about.
note how since we used w! file rather than w ! cat > file, vi/ex now  
has switched to using that name. Okay, enough mucking about.
!e
!
:q
$ echo And let\'s review what we have in our files.
And let's review what we have in our files.
$ ls
32eyxG6upe9cEXVusggpxzFmp9M.BAK  reasonable_filename
MQSDvZtaN6wEj7yr8dQ8VYu0qU       reasonable_filename.duplicate
$ (set -x; cmp reas* && echo MATCHED)
+ cmp reasonable_filename reasonable_filename.duplicate
+ echo MATCHED
MATCHED
$ more $(ls | grep -v '\.duplicate$') | cat
::::::::::::::
32eyxG6upe9cEXVusggpxzFmp9M.BAK
::::::::::::::
original content
::::::::::::::
MQSDvZtaN6wEj7yr8dQ8VYu0qU
::::::::::::::
this filename (MQSDvZtaN6wEj7yr8dQ8VYu0qU) sucks!
::::::::::::::
reasonable_filename
::::::::::::::
original content
Let's say I add some text to this file, and I'm editing along ...
Then I think, oh my gosh, I didn't save the original, I don't want to
clobber it.  Well, no need to get out of ex/vi.  Let's say I want to
save a copy of the original, with same name, but with .BAK appended.
Okay, easy enough ... even with filename that sucks ...
Haven't written out my changes (yet), so ... actually instead,
let's say I want to move/rename the original to having a .BAK extension,
and write what I'm editing to a reasonable_filename.  Again, no need
to leave vi/ex.
Now let's add some more notes.
I intentionally did:
w !cat > reasonable_filename
rather than:
w! reasonable_filename
at least at that point, as I didn't (yet) want vi/ex to "remember"
the new filename reasonable_filename, so I could still use % for the
shortcut to (expansion of) the filename I/vi/ex was working on (and
likewise # if I have/had an alternate filename).  So, where were we,
ah, appending notes.  I also did the
n! r* M*
to start editing a new set of files - here vi/ex (not shell) does
shell-type metasyntax expansion/matching of filenames for us.  I can see
the files with arg:
Let's write this out and go on to next.
$

Also, in the above, you may notice a wee little trick with more.
If more has multiple file (non-option) arguments, and it's output
isn't a tty device, it will simply output each file, preceded with
a header identifying the name of the file.

http://linuxmafia.com/pipermail/sf-lug/2020q4/015078.html
http://linuxmafia.com/pipermail/sf-lug/2020q4/015081.html

So, in summary, it's ex, not the shell, that does some of these substitutions
for us, notably % for current file, # for alternate file,
shell file name globbing, and substitution of ! in shell commands.
E.g.:
$ rm * && ex foo bar
foo: 2 files to edit: new file: line 1
:$r !echo current file is %
!e
current file is foo
:w
foo: new file: 1 lines, 20 characters
:n
bar: new file: line 1
:$r !echo now current file is % and alternate #, and prior command: !
!e
now current file is bar and alternate foo, and prior command: echo  
current file
is foo
:w
bar: new file: 1 lines, 87 characters
:!mv foo FOO; mv bar BAR
!
:n! *
BAR: 2 files to edit: unmodified: line 1
:arg
[BAR] FOO
:q!
$




More information about the sf-lug mailing list