10

Like most programmers, I perform a lot of repetitive tasks. In optimising my workflow, I'm taking some of those repetitive tasks, and refactoring them into shell scripts.

One thing that I'm trying to automate is the recreation of PostgreSQL views. I have the following view.

create or replace view person as
select
    1 as person_id
    , 'John'::text as first_name,
    'Doe'::text as last_name;

I can dump this view with psql -c "\\d+ person" and the output is as follows:

                   View "public.person"
   Column   |  Type   | Modifiers | Storage  | Description 
------------+---------+-----------+----------+-------------
 person_id  | integer |           | plain    | 
 first_name | text    |           | extended | 
 last_name  | text    |           | extended | 
View definition:
 SELECT 1 AS person_id,
    'John'::text AS first_name,
    'Doe'::text AS last_name;

I can reformat this text into a CREATE OR REPLACE VIEW statement with the following keystrokes in vim: gg0df"iCREATE OR REPLACE VIEW <ESC>$cl AS<ESC>j0d/^View definition:<CR>ddG$:wq (I've reformatted <ESC>, etc, in the above).

I've got the above working perfectly, except for the screen "flashes" that occur. For example, if at the shell I type a=$(psql -c "\\d+ person" | vim -s <vimscriptfile> -); echo "$a" then my screen "flashes" before outputting the nicely format SQL.

Is there any way to remove this flash? Or is there a better using-vim-in-a-pipeline approach than what I'm employing?

magnus
  • 427
  • 3
  • 10
  • What is pg-recreate-view? If it is using Vim somehow, you might want to use the -Nes options with it. – muru Feb 13 '17 at 04:46
  • Just a wrapper function around the psql call. – magnus Feb 13 '17 at 04:52
  • 1
    Um, this is possible a stupid question, but you don't seem to be using Vim in your example shell command? – Rich Feb 13 '17 at 09:54
  • @Rich "I can reformat this text into a CREATE OR REPLACE VIEW statement with the following keystrokes in vim:" - First sentence right after the second big preformatted block. – 8bittree Feb 13 '17 at 16:47
  • 1
    But that's not using Vim as a stream editor... that's typing commands into Vim manually. So is the problem simply that you don't know how to use Vim as a stream editor? If so, what do you mean by "I've got the above working perfectly, except for the screen "flashes" that occur."? – Rich Feb 13 '17 at 17:43
  • 2
    What I don't understand is, does this answer your question, or is your question actually about a problem with the method described in there? – Rich Feb 13 '17 at 17:44
  • @Rich, I believe I am doing what your referenced question does, but vim still takes over my terminal for about 1/10th of a second, and when wrapping it in shell substitution (e.g., a=$(psql ... | vim -s ... -)) then echoing that value causes the screen to flash. I think I must be doing something wrong. – magnus Feb 13 '17 at 21:51
  • Your edit makes things a lot clearer! Unfortunately, it also means I'm unable to help you. :( – Rich Feb 14 '17 at 09:32
  • 2
    Perhaps a Neovim expert could help here; sounds like something a headless instance could be used for. https://neovim.io/community/ –  Feb 17 '17 at 15:12
  • I'm pretty sure the correct answer is 'use sed instead'. However, maybe vipe might be of use? – evilsoup Feb 17 '17 at 18:07
  • 1
    Vim has a stream-editing mode, called ex mode. It is possible to start vim in ex mode, and it is possible to switch to it after opening a file (:ex). (:vi will switch back to visual mode). I've never used ex mode, but it might be what you're looking for. Maybe replacing vim in your command with ex would be enough? – jpaugh Jul 17 '17 at 20:02

2 Answers2

4

To avoid screen flashes when editing stream non-interactively, you need to start Vim in Ex mode by adding -e (Ex mode) or -E (improved Ex mode) into your command-line arguments.

Here is the simple example editing text from the standard output and printing the result into standard output:

$ echo "foo bar" | vim -E +%s/foo.// +%p -cq! /dev/stdin
bar

To parse your output, you may try the following syntax:

$ psql -c "\\d+ person" | \
  ex +'1s/\s\+View "\([^"]\+\)"/CREATE OR REPLACE VIEW \1 AS/g' +'2,/View.definition/d' +%j +%p -scq! /dev/stdin

The output should look like:

CREATE OR REPLACE VIEW public.person AS SELECT 1 AS person_id, 'John'::text AS first_name, 'Doe'::text AS last_name;

Explanation:

  • Using ex command is equivalent to run Vim in Ex mode (vim -e).
  • +'cmd'/-c {cmd} - Invokes Ex command.
  • -cq! - Forcibly quit Vim as we're not saving any files.
  • -s - Run in silent mode (prevents extra screen flashes).
  • /dev/stdin - We use /dev/stdin as an input file, because using - doesn't work properly.
  • 1s/pattern/something/g - Substitute pattern with something in the 1st line.
  • 2,/pattern/d - Delete content starting from the 2nd line to the line with pattern.
  • %j - Joins all the lines together.
  • %p - Prints the buffer to the console output.
kenorb
  • 18,433
  • 18
  • 72
  • 134
  • 1
    The following package should also help you do this without the extra flags: https://github.com/MilesCranmer/vim-stream – Miles Apr 23 '19 at 13:59
0

I was not able to reproduce your macro but here is a (far from ideal) approach to your problem: Register persistently your steps in a macro and then launch Vim and tell it to directly execute your macro and close again.

Ex: In your .vimrc, you register the macro:

" convert everything to uppercase
let @x = 'ggVGU'

Start Vim, call the macro from the cmd line and close Vim again:

$ cat test.txt
sdfas;dlfasdf
asdf
adf

$ vim -c ":norm! @x" -c ":x" test.txt

$ cat test.txt
SDFAS;DLFASDF
ASDF
ADF

This will obviously start vim and close it right a way. I guess there are some optimizations possible. Make a shell alias for the whole thing, start Vim without plugins/vimrc and only load your dummy vimrc (to make startup fast if that's a problem), probably other ideas as well.

This answer explains how to record macros and add them to your .vimrc.

Rolf
  • 265
  • 1
  • 8
  • Incidentally, you can yank text into a macro register (e.g. "ay to yank a macro into a), and then run it (@a). You just have to be careful not to yank the newline character at the end, which yy does. – jpaugh Jul 17 '17 at 19:55
  • This doesn't seem to answer the question, which is asking how to use Vim as a stream editor. How would you use your solution in a pipeline, and how does it improve on the existing solution the question-asker describes? – Rich Nov 14 '17 at 22:20