2

I'm big on scripted file edits using POSIX ex rather than sed -i and co. The big trick of course is how to ensure ex doesn't hang waiting for input if an error is encountered.

I just wrote a comment about the possibility of using ex to edit a file conditionally, and only save a backup of the file if edits were made.

It seems that if there were some option which could be used to tell ex "In case of any error, q!" then this would be fairly simple conceptually:

ex -sc '%s/pattern/replace/g | !cp % %.bup' -cx file

Of course that copy command will fail on any special characters in the filename, but you get the concept; a better form would be:

ex -sc '/pattern/ | w %.bup' -c '%s//replace/g | x' file

Essentially what I'm asking for is a form of conditional logic in ex batch scripting, such that "If this pattern isn't found, q!; otherwise continue."

How can I do this with ex?

  • 1
    Interesting that the reverse case of automatically exiting if a pattern is found can be easily accomplished with e.g. ex -sc 'g/exit_pattern/q!' -c '%s/change/stuff/g | x' file, but this method doesn't adapt at all to my question as it actually operates line by line. – Wildcard Mar 17 '16 at 9:54
  • 1
    I don't know if it will help, but you could make the substitution, check if the buffer is modified, if it is undo and make a backup, then redo the substitution. It would give something like: vim -c '%s/pattern/replace/ge | exe &modified == 1 ? "undo | w %.bup | redo | write | exit" : "exit"' file – saginaw Mar 17 '16 at 10:19
  • It's fairly close, except for being not POSIX. Would you mind adding it as an answer? ;) – Wildcard Mar 17 '16 at 10:22
2

Maybe you could make the substitution and check if the buffer is modified by looking at the value of the option 'modified'.
If it is modified, undo, make a backup, redo and exit.
Otherwise, if the buffer is not modified, simply quit. It could give something like:

vim -c '%s/pattern/replace/ge | exe &mod ? "undo | w %.bup | redo | x" : "q"' file

But as you said in the comments, it's probably not POSIX.

| improve this answer | |
  • I didn't realize that the :w command would be correctly terminated by a |—I was thinking of sed, wherein a w command can't be terminated by a ; but only by a newline. And this is still helpful even though not POSIX; thanks! – Wildcard Mar 17 '16 at 10:36
  • 1
    (Given that you use vim as the command rather than ex or vi, it's rather self-protecting against POSIX incompatibility. If the user doesn't have vim but only some minimal POSIX implementation, the vim command won't even be found and they'll get an error at the shell level.) – Wildcard Mar 17 '16 at 10:37
2

Since posting this question I've learned the trick of wrapping automated ex edits in pipelines, with printf used to send the actual commands. This has the advantage that ex will not hang on error conditions.

The way I would do this now for a portable script would be:

  1. Make a copy (backup) of the file.
  2. Do the edits using printf to pipe commands to ex.
  3. Delete the backup if it is identical to the edited file (i.e. no changes have been made).

Simplistically, this would look like:

cp file file.bak
printf '%s\n' '%s/pattern/replace/g' x | ex file
cmp -s file file.bak && rm file.bak

More robustly, I might do it like so:

#!/bin/sh
[ -e file.bak ] && exit 1
[ -w file ] && [ -r file ] || exit 1
cp file file.bak
printf '%s\n' '%s/pattern/replace/g' x | ex file
cmp -s file file.bak && rm file.bak
| improve this answer | |
2

You could store whether to exit or not in a register, then execute it:

" store q! in register x
s/$/^Mq!/ | d x
"
" if pattern exists, store " in register x
g/pattern/ s/$/^M"/ | d x
"
" execute :q! if pattern wasn't found, :" if it was
@x
"
w %.bup
"
" ... more edits ...
"
wq

You probably don't want to use ex for any complicated control flow though. Maybe use a more fully featured programming language. Or in this case you could grep for the pattern first:

$ grep pattern file && ex -c 'commands' file
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.