Get rid of ^# in vim output from system(...) - vim

I am trying to add the result of a system() call as a header for one of the plugins I use (vim-startify). The command is:
system('vim --version | head -1')
However when it gets printed it has an extra character in the end: ^#
VIM - Vi IMproved 8.0 (2016 Sep 12, compiled Feb 12 2017 23:20:57)^#
There is a similar question here, but it addresses a file, not a buffer.

With Vim 8 (or late 7.4 versions):
get(systemlist('vim --version'), 0)

Just chomp it: system('whatever')[:-2], or, if you prefer something more robust—something that won't misbehave should the newline be missing— you can apply: substitute(system_result, "\n\\+$", "", "").
Note that, unlike tr, this solution works on all platforms. That's what we used to do with the chomp() function in Perl.

This should give you what you want:
system('vim --version | head -1 | tr -d "\n"')
See $ man tr.

Related

How can I set path of vim from the shell command result

I'd like to set c++ header file path automatically from .vimrc.
So I tried to write .vimrc file as shown below:
let g:gcpp_headers_path=system("g++ --version | grep g++ | awk '{print \"/usr/include/c++/\"$NF}'")
"if isdirectory(gcpp_headers_path)
set path+=g:gcpp_headers_path
"endif
from the vimrc above I found the vim variable path set as shown below:
:set path?
path=.,/usr/include,,,g:gcpp_headers_path
The wanted output is as shown below:
:set path?
path=.,/usr/include,/usr/include/c++/7.5.0
I'd like to get this reviewed. Could someone advise me?
F.Y.I. The command in system() is shown below:
~$ g++ --version | grep g++ | awk '{print "/usr/include/c++/"$NF}'
/usr/include/c++/7.5.0
For documentation I am writing my answer found here:
let g:gcpp_headers_path = system("g++ --version | grep g++ | awk '{print \"/usr/include/c++/\"$NF}'")
execute 'set path+=' . g:gcpp_headers_path
My vim version is Vi IMproved 8.0 (2016 Sep 12, compiled Jan 20 2022 02:47:53)
I raised new post for Windows
:set doesn't evaluate expressions so you can't use variables directly. You have two solutions.
Use :help :let instead, which can evaluate expressions:
:let &path .= ',' . g:gcpp_headers_path
Notes:
the & before the option name,
the different operator, because we are concatenating the original value, a string, with another string,
the ,, needed to separate your custom path from what's already there,
the . concatenation operator.
Use :help :execute to build the proper :set command before executing it:
:execute 'set path+=' . g:gcpp_headers_path

How to fzf recent files of vim/nvim, not inside vim but from terminal

I know how to fzf.vim, but I'd like to open from terminal.
Grepping history or viminfo may be achieve thst, but I wonder if there is any smart way.
This is how you can save the list of recent files from vim to a file:
vim -c "call append(0, v:oldfiles)" -c "write vim-oldfiles.tmp" -c exit
Put v:oldfiles (the list of recent files saved in ~/.viminfo) into the first (new and empty at the start) buffer, write the buffer to a file, exit.
Now you can pass the content of file to fzf.
Not exact solution. But you could open a terminal buffer on the lower part of your vim edit like an IDE and use your terminal fzf
However, not sure if this will let you open a file in a new vim tab
I have an zsh autoloaded function called old:
function old(){
vim -c 'redir >> /tmp/oldfiles.txt | silent oldfiles | redir end | q'
sed -i '/NvimTree$/d' /tmp/oldfiles.txt
local fname
fname=$(awk '/home/ && !/man:/ {print $2}' /tmp/oldfiles.txt | fzf) || return
vim "$fname"
\rm /tmp/oldfiles.txt
}
If you're having trouble executing vim on files that have ~ in their path (vim open a new blank file instead of the desired file) because fzf and vim don't expand tilde (~), here's how I do it:
export FZF_DEFAULT_OPTS=$FZF_DEFAULT_OPTS"
--bind 'ctrl-e:execute(vim -c \"execute \\\"edit\\\" expand({})\" >/dev/tty)'
"
It's trial and error, based on this.
Combining some of the other answers, here's a version that does not need a temporary file and writes to stdout (so you can pipe this into another command, or capture the output using $(...)).
vim -e -c "redir >> /dev/fd/100 | for f in v:oldfiles | silent echo substitute(f, \"^\\\\~\", \$HOME, \"g\") | endfor | redir end | q" 100>&1 &>/dev/null
This solution combines elements from other solutions, but with some improvements:
It uses some shell redirection to duplicate stdout to some free fd (100>&1) and then uses /dev/fd/100 to force writing output there. This ensures that vim actually writes to stdout rather than the terminal. Note that this can also be made to work using /dev/fd/1 (but only when omitting redir end for some reason), but then we cannot apply the next point.
It redirects stdout (and for good measure) also stderr to /dev/null, to prevent vim writing some terminal escape codes to stdout on startup, so using a different fd ensures clean output.
It uses vim in "ex" mode (vim -e) to suppress the "Vim: Warning: Output is not to a terminal" output and accompanying delay. [source]
It uses a for-loop to iterate over v:oldfiles to output just the filenames (the oldfiles command used by https://stackoverflow.com/a/70749181/740048 adds line numbers).
It uses a substitute to expand ~ in the filenames returned by vim (making the returned filenames easier to proces. Normally, shells like bash expand ~ in arguments passed to commands, but this happens only for tildes in the command typed, not tildes that result from variables or command substitution. To prevent having to rely on unsafe eval'ing later, better to expand (just) the tildes beforehand.
I also tried using the append / write combo from https://stackoverflow.com/a/60018642/740048, which worked with the /dev/fd/100 trick, but then ended up putting /dev/fd/100 in the list of oldfiles, so I did not use that approach.

Command sed using g

I am new to Linux.
I was debugging some code. I encountered the following command:
PROGRAM_ID=$(echo $PROGRAM_ID|sed 's/-/,/g')
Can anybody explain what the g represents here?
I understand hyphen is being replaced with comma.
The /g flag means, perform the substitution globally on a line. Without that flag, only the first hyphen on every line would get substituted.
A better way with Bash would be
PROGRAM_ID=${PROGRAM_ID//-/,}
but if you have to be portable to Bourne shell in general, this replacement facility is not available.
(In which case you should take care to keep "$PROGRAM_ID" in double quotes in the echo.)
Its easy to see how g (global) works with these two example:
echo "test-one-two-three" | sed 's/-/,/g'
test,one,two,three
echo "test-one-two-three" | sed 's/-/,/'
test,one-two-three
Without the g it only replace the first hit.

Applying a patch to files with spaces in names

Here's an output of diff -u "temp temp/docs 1.txt" "temp temp/docs 2.txt":
--- temp temp/docs 1.txt Mon Apr 7 16:15:08 2014
+++ temp temp/docs 2.txt Mon Apr 7 16:18:45 2014
## -2,6 +2,6 ##
22
333
4444
-555555
+55555
666666
7777777
However, feeding this diff to patch -u fails with following message:
can't find file to patch at input line 3
Perhaps you should have used the -p or --strip option?
The text leading up to this was:
--------------------------
|--- temp temp/docs 1.txt Mon Apr 7 16:15:08 2014
|+++ temp temp/docs 2.txt Mon Apr 7 16:18:45 2014
--------------------------
Apparently, the spaces are the problem; is there a way to make patch to work on files with spaces in names?
No, GNU patch doesn't support this. Here's the official statement: http://www.gnu.org/software/diffutils/manual/html_node/Unusual-File-Names.html#Unusual%20File%20Names
Gnu patch 2.6.1 (linux) seems to obey at least 1 space (not tried with more) if the filename is separated from the date with tab.
YYMV
I encountered the same problem when trying to establish conventions how to do manual version control with diff and patch.
I found out that GNU "diff" creates quoted path names in the patch headers if they contain spaces, while BusyBox "diff" doesn't.
Neither GNU nor BusyBox "patch" accepts quoted path names.
If the problem is just embedded spaces within filenames, it can therefore be avoided by using "busybox patch" rather than GNU "patch".
Another solution is to postprocess the output of GNU "diff" before feeding it into "patch":
sed 's,^\([-+]\{3\} \)"\([^"]*\)",\1\2,' $PATCHFILE | patch -p1
This works whether $PATCHFILE was created with GNU or busybox diff, but will only work with unified diff format.
Unfortunately, it turns out that leading or trailing spaces in filenames cannot be preserved with this method, as "patch" will skip them when parsing the path names from the patch instructions.
The approach will neither work if the filename starts with a literal double quote - but then, who uses such file names?
Most of the time, however, the above approach works just fine.
Finally a note of other approaches I have also tried but which did not work:
First I tried to replace the quotation of the whole path names by individually quoted path name components. This failed because "patch" does not use double quotes as meta-characters at all. It considers them to be normal literal characters.
Then I tried to replace all spaces by "\040" like CVS does - but "patch" does not seem to accept octal-escapes either, and this failed too.

Is there different between single quote and double quote in vim command mode?

In my vim, I can use :%!sed "s/^/ /", got the wrong output when I use :%!sed 's/^/ /' .
sed: -e expression #1, char 0: no previous regular expression
Is there differences between single quote and double quote in vim command mode?
In my sed, single quote is the same as double quote.
$ echo "wha012" | sed 's/w/haha/'
hahaha012
$ echo "wha012" | sed "s/w/haha/"
hahaha012
my system is xp+vim 7.3 for windows.
In my system:
[1] "c://cygwin/bin/ash.exe"
[2] "c://cygwin/bin/bash.exe"
[3] "c://cygwin/bin/dash.exe"
[4] "c://cygwin/bin/sh.exe"
if i set set shell=\"c:\cygwin\bin\sh.exe"\ -f in _vimrc,i get the new wrong messages:
sed command can not found.
Funny, when I try :%!sed "/^/ /" I get the same error message as when I use single quotes:
sed: 1: "/^/ /": invalid command code /
(This line replaces the content of my file.) I expect to get an error message there because, as #Birei pointed out, you left out the sed s command. This works as expected, with either single or double quotes:
:%!sed "s/^/ /"
#Birei is also right that you can use vim to do things like this, but I assume you have simplified the example from what you were really trying to do.
To answer the original question, Vim uses single quotes for literal strings. The only special character in a literal string is ' itself. Strings delimited with double quotes use \ to denote special character, such as `"\<Esc>".
:echo 'a''b' == "a'b"
:help expr-string
:help literal-string
my system is xp+vim 7.3 for windows
By default Vim uses cmd.exe to run :! commands on Windows, which behaves differently with regard to quoting from the POSIX shell that your s/w/haha/ examples suggest you've been testing with. Try something like
:set shell=\"C:\path\to\sh.exe\"\ -f
to tell it to use your POSIX shell instead. Or if you're using cygwin then try the cygwin version of vim instead of the Windows native one.
The difference is in the sed command, that lets interpolate variables when you execute it directly from the shell, like:
sed "s/$pattern/$replacement/"
but your problem is that you have to use a substitution command that begins with letter s, like:
:%!sed "s/^/ /"
Also you can have same behaviour inside vim without an external command, like:
:%s/^/ /

Resources