how to tell when vim has added EOL character - vim

How I can see in vim when a file has a newline character at the end? It seems vim always shows one, whether it's truly there or not.
For example, opening a file that has a newline at the end:
echo "hi" > hi
# confirm that there are 3 characters, because of the extra newline added by echo
wc -c hi
# open in binary mode to prevent vim from adding its own newline
vim -b hi
:set list
This shows:
hi$
Now by comparison, a file without the newline:
# prevent echo from adding newline
echo -n "hi" > hi
# confirm that there are only 2 characters now
wc -c hi
# open in binary mode to prevent vim from adding its own newline
vim -b hi
:set list
Still shows:
hi$
So how can I see whether a file truly has a newline at the end or not in vim?

Vim stores this information in the 'endofline' buffer option. You can check with
:setlocal eol?

In the second case, Vim displays [noeol] when loading the file.
Don't you see the [noeol] output, when loading that file?

Vim warns you when it opens/writes a file with no <EOL> at the end of the last line:
"filename" [noeol] 4L, 38C
"filename" [noeol] 6L, 67C written

Related

how to visualise and delete trailing newline at the end of file in vim\nvim

Sometimes I need to edit files which should not end with a newline.
However vim\nvim by default do not visualise in any way the newline character at the end of file. Therefore I am not able to:
visually confirm if the file has a newline character at the end or not
remove that character
Are there any setting which would allow me to see the tailing newline character and edit it in the same way as any other characters?
For example, after create 2 files as follows:
echo test > file-with-newline
echo -n test > file-without-newline
opening first one with nvim file-with-newline shows:
test
~
~
file-with-newline
opening second one with nvim file-without-newline shows:
test
~
~
file-without-newline
Navigating with the cursor to the end of line in either case yields the same result (the cursor stops after last visible character: t). There is no way to tell if the newline is there or not, let alone remove it using familiar commands used to remove ordinary characters (or newlines within the file).
You can enable the option :help 'list':
:set list
to show that "newline character" as a $ at the end of the line (among other things):
Note, however, that the option doesn't make the character "editable" in any way.
if the file has a newline character at the end or not
:set eol?
endofline
remove that character
:set noeol nofixeol
:update

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.

How to write a string in a file using vim editor from command line

I want to create a new file using vi editor from command line and add a string to it multiple times say 100. Using vi -S command.script file.txt is supposed to do the trick where a new file file.txt will be created and the commands given in command.script file can write to this file. My command.script contains
:%100a hello world
:wq
But its's not working, what I am doing wrong?
If you interactively execute :%100a hello world in a Vim session, you'll get E488: Trailing characters. Looking up :help :a:
:{range}a[ppend][!] Insert several lines of text below the specified
line. If the {range} is missing, the text will be
inserted after the current line. [...]
These two commands will keep on asking for lines, until you type a line
containing only a ".".
tells you that the text has to be put in following lines (and concluded by a line with only a . character).
Or did you mean to use the normal mode a command? (That one takes a [count] to multiply; your %100 range is wrong, too!)
You can also use the low-level function append(), repeating the string with repeat().
summary
$append
hello world
[...]
hello world
.
execute "$normal! 100ahello world\<CR>"
" Easier with o instead of a:
$normal! 100ohello world
call append('$', repeat(['hello world'], 100))
non-Vim alternatives
But honestly, if that is your real use case (and not just a simplified toy example), you don't need Vim at all for this. Here's one example for the Bash shell:
$ for i in $(seq 100); do echo "hello world" >> file.txt; done

vi, find line and output/output to separate file

I'm looking for a command in VI/VIM to search for particular text in the file and grab the whole line and output in either on the screen or separate file. e.g.
This is some line with this _word_ and some other text.
This is some line with this some other text and some other text.
so this would output only the first line..
:redir > output.txt
:g/_word_/p
:redir END
The line will be output to the screen and to output.txt. See :h :redir.
EDIT: I agree with others who suggest using plain old *nix grep if you can. Situations where you might not be able to use grep:
You're searching buffer text that doesn't exist in a file on the filesystem.
You're using Vim-specific regex extensions, like cursor location, column number, marks, etc.
You want do this in a cross-platform way, and grep might not exist on the system you're using.
redir can be useful in these situations.
use the g (global) command:
:g/_word_/y
will yank all lines containing _word_
having mentioned the DOS find command, you probably want to use grep:
grep -h '_word_' * > results
in vi, from command mode
search for _word_
/_word_
yank the line
yy
paste the line
p

How to find out line-endings in a text file?

I'm trying to use something in bash to show me the line endings in a file printed rather than interpreted. The file is a dump from SSIS/SQL Server being read in by a Linux machine for processing.
Are there any switches within vi, less, more, etc?
In addition to seeing the line-endings, I need to know what type of line end it is (CRLF or LF). How do I find that out?
You can use the file utility to give you an indication of the type of line endings.
Unix:
$ file testfile1.txt
testfile.txt: ASCII text
"DOS":
$ file testfile2.txt
testfile2.txt: ASCII text, with CRLF line terminators
To convert from "DOS" to Unix:
$ dos2unix testfile2.txt
To convert from Unix to "DOS":
$ unix2dos testfile1.txt
Converting an already converted file has no effect so it's safe to run blindly (i.e. without testing the format first) although the usual disclaimers apply, as always.
Ubuntu 14.04:
simple cat -e <filename> works just fine.
This displays Unix line endings (\n or LF) as $ and Windows line endings (\r\n or CRLF) as ^M$.
In vi...
:set list to see line-endings.
:set nolist to go back to normal.
While I don't think you can see \n or \r\n in vi, you can see which type of file it is (UNIX, DOS, etc.) to infer which line endings it has...
:set ff
Alternatively, from bash you can use od -t c <filename> or just od -c <filename> to display the returns.
In the bash shell, try cat -v <filename>. This should display carriage-returns for windows files.
(This worked for me in rxvt via Cygwin on Windows XP).
Editor's note: cat -v visualizes \r (CR) chars. as ^M. Thus, line-ending \r\n sequences will display as ^M at the end of each output line. cat -e will additionally visualize \n, namely as $. (cat -et will additionally visualize tab chars. as ^I.)
Try file, then file -k, then dos2unix -ih
file will usually be enough. But for tough cases try file -k or dosunix -ih.
Details below.
Try file -k
Short version: file -k somefile.txt will tell you.
It will output with CRLF line endings for DOS/Windows line endings.
It will output with CR line endings for MAC line endings.
It will just output text for Linux/Unix "LF" line endings. (So if it does not explicitly mention any kind of line endings then this means: "LF line endings".)
Long version see below.
Real world example: Certificate Encoding
I sometimes have to check this for PEM certificate files.
The trouble with regular file is this: Sometimes it's trying to be too smart/too specific.
Let's try a little quiz: I've got some files. And one of these files has different line endings. Which one?
(By the way: this is what one of my typical "certificate work" directories looks like.)
Let's try regular file:
$ file -- *
0.example.end.cer: PEM certificate
0.example.end.key: PEM RSA private key
1.example.int.cer: PEM certificate
2.example.root.cer: PEM certificate
example.opensslconfig.ini: ASCII text
example.req: PEM certificate request
Huh. It's not telling me the line endings. And I already knew that those were cert files. I didn't need "file" to tell me that.
Some network appliances are really, really picky about how their certificate files are encoded. That's why I need to know.
What else can you try?
You might try dos2unix with the --info switch like this:
$ dos2unix --info -- *
37 0 0 no_bom text 0.example.end.cer
0 27 0 no_bom text 0.example.end.key
0 28 0 no_bom text 1.example.int.cer
0 25 0 no_bom text 2.example.root.cer
0 35 0 no_bom text example.opensslconfig.ini
0 19 0 no_bom text example.req
So that tells you that: yup, "0.example.end.cer" must be the odd man out. But what kind of line endings are there? Do you know the dos2unix output format by heart? (I don't.)
But fortunately there's the --keep-going (or -k for short) option in file:
$ file --keep-going -- *
0.example.end.cer: PEM certificate\012- , ASCII text, with CRLF line terminators\012- data
0.example.end.key: PEM RSA private key\012- , ASCII text\012- data
1.example.int.cer: PEM certificate\012- , ASCII text\012- data
2.example.root.cer: PEM certificate\012- , ASCII text\012- data
example.opensslconfig.ini: ASCII text\012- data
example.req: PEM certificate request\012- , ASCII text\012- data
Excellent! Now we know that our odd file has DOS (CRLF) line endings. (And the other files have Unix (LF) line endings. This is not explicit in this output. It's implicit. It's just the way file expects a "regular" text file to be.)
(If you wanna share my mnemonic: "L" is for "Linux" and for "LF".)
Now let's convert the culprit and try again:
$ dos2unix -- 0.example.end.cer
$ file --keep-going -- *
0.example.end.cer: PEM certificate\012- , ASCII text\012- data
0.example.end.key: PEM RSA private key\012- , ASCII text\012- data
1.example.int.cer: PEM certificate\012- , ASCII text\012- data
2.example.root.cer: PEM certificate\012- , ASCII text\012- data
example.opensslconfig.ini: ASCII text\012- data
example.req: PEM certificate request\012- , ASCII text\012- data
Good. Now all certs have Unix line endings.
Try dos2unix -ih
I didn't know this when I was writing the example above but:
Actually it turns out that dos2unix will give you a header line if you use -ih (short for --info=h) like so:
$ dos2unix -ih -- *
DOS UNIX MAC BOM TXTBIN FILE
0 37 0 no_bom text 0.example.end.cer
0 27 0 no_bom text 0.example.end.key
0 28 0 no_bom text 1.example.int.cer
0 25 0 no_bom text 2.example.root.cer
0 35 0 no_bom text example.opensslconfig.ini
0 19 0 no_bom text example.req
And another "actually" moment: The header format is really easy to remember: Here's two mnemonics:
It's DUMB (left to right: d for Dos, u for Unix, m for Mac, b for BOM).
And also: "DUM" is just the alphabetical ordering of D, U and M.
Further reading
man file
man dos2unix
Wikipedia: Newline
To show CR as ^M in less use less -u or type -u once less is open.
man less says:
-u or --underline-special
Causes backspaces and carriage returns to be treated as print-
able characters; that is, they are sent to the terminal when
they appear in the input.
You can use xxd to show a hex dump of the file, and hunt through for "0d0a" or "0a" chars.
You can use cat -v <filename> as #warriorpostman suggests.
You may use the command todos filename to convert to DOS endings, and fromdos filename to convert to UNIX line endings. To install the package on Ubuntu, type sudo apt-get install tofrodos.
You can use vim -b filename to edit a file in binary mode, which will show ^M characters for carriage return and a new line is indicative of LF being present, indicating Windows CRLF line endings. By LF I mean \n and by CR I mean \r. Note that when you use the -b option the file will always be edited in UNIX mode by default as indicated by [unix] in the status line, meaning that if you add new lines they will end with LF, not CRLF. If you use normal vim without -b on a file with CRLF line endings, you should see [dos] shown in the status line and inserted lines will have CRLF as end of line. The vim documentation for fileformats setting explains the complexities.
Also, I don't have enough points to comment on the Notepad++ answer, but if you use Notepad++ on Windows, use the View / Show Symbol / Show End of Line menu to display CR and LF. In this case LF is shown whereas for vim the LF is indicated by a new line.
I dump my output to a text file. I then open it in notepad ++ then click the show all characters button. Not very elegant but it works.
Vim - always show Windows newlines as ^M
If you prefer to always see the Windows newlines in vim render as ^M, you can add this line to your .vimrc:
set ffs=unix
This will make vim interpret every file you open as a unix file. Since unix files have \n as the newline character, a windows file with a newline character of \r\n will still render properly (thanks to the \n) but will have ^M at the end of the file (which is how vim renders the \r character).
Vim - sometimes show Windows newlines
If you'd prefer just to set it on a per-file basis, you can use :e ++ff=unix when editing a given file.
Vim - always show filetype (unix vs dos)
If you want the bottom line of vim to always display what filetype you're editing (and you didn't force set the filetype to unix) you can add to your statusline with
set statusline+=\ %{&fileencoding?&fileencoding:&encoding}.
My full statusline is provided below. Just add it to your .vimrc.
" Make statusline stay, otherwise alerts will hide it
set laststatus=2
set statusline=
set statusline+=%#PmenuSel#
set statusline+=%#LineNr#
" This says 'show filename and parent dir'
set statusline+=%{expand('%:p:h:t')}/%t
" This says 'show filename as would be read from the cwd'
" set statusline+=\ %f
set statusline+=%m\
set statusline+=%=
set statusline+=%#CursorColumn#
set statusline+=\ %y
set statusline+=\ %{&fileencoding?&fileencoding:&encoding}
set statusline+=\[%{&fileformat}\]
set statusline+=\ %p%%
set statusline+=\ %l:%c
set statusline+=\
It'll render like
.vim/vimrc\ [vim] utf-8[unix] 77% 315:6
at the bottom of your file
Vim - sometimes show filetype (unix vs dos)
If you just want to see what type of file you have, you can use :set fileformat (this will not work if you've force set the filetype). It will return unix for unix files and dos for Windows.

Resources