Question: [If you open a file in `vim` and that file has no `EOL` at the end of its last line, then the editor will report it as `[noeol]`.](https://unix.stackexchange.com/questions/31807/what-does-the-noeol-indicator-at-the-bottom-of-a-vim-edit-session-mean) How can I determine this *before* opening it in `vim`? (Is there a command I can issue to determine this?)

Answer: 
`tail -c 1` outputs the last character (more precisely, the last byte) of its input.

Command substitution strips off a trailing newline, so `$(tail -c 1 <…)` is *empty* if the last character of the file is a newline. It's also empty if the last character is a null byte (in most shells), but text files don't have null bytes.

Keep in mind that an empty file doesn't need an extra newline.

```
if [ ! -s "$filename" ]; then
  echo "$filename is empty"
elif [ -z "$(tail -c 1 <"$filename")" ]; then
  echo "$filename ends with a newline or with a null byte"
else
  echo "$filename does not end with a newline nor with a null byte"
fi

```
Create a file with only a newline character in it, then compare it to the last byte of your file:

```
printf '\n' > newline_only
tail -c 1 your_file | cmp -s newline_only -

```

I used `cmp -s` to make `cmp` basically silent. The exit status `0` indicates there is a newline character at the very end of `your_file`.

If `your_file` is empty, the exit status will be `1`. You may want to make an exception and get `0` in this case. If so, *prepend* a newline to what `cmp` gets via its stdin:

```
( printf '\n'; tail -c 1 your_file ) | cmp -s newline_only -
# or
cat newline_only your_file | tail -c 1 | cmp -s newline_only -
# or better
<your_file cat newline_only - | tail -c 1 | cmp -s newline_only -

```

The last one is somewhat better because it will return non-zero exit status if `your_file` doesn't exist, cannot be read etc. If I were you I would want this. Although if `your_file` is in fact a directory then `cat`, `tail` and `cmp` will run and you may get `0` and a complaint from `cat`; or may not, see this: [*When did directories stop being readable as files?*](https://unix.stackexchange.com/q/154119/108618)). Therefore you may want some *additional* logic or option (e.g. `set -o pipefail` in Bash).

Notes:

* In some shells you can use process substitution to avoid creating the `newline_only` file. It will be like:

```
# e.g. in Bash
< your_file cat <(printf '\n') - | tail -c 1 | cmp -s <(printf '\n') -

```
* `tail` reading from a pipe cannot seek. `cat` needs to read the entire `your_file`, only then `tail` can do its job. `tail -c 1 your_file` or `<your_file tail -c 1` may be smart enough to seek within `your_file`. This should be negligible if you test one or few *small* files though.
* [This other solution](https://unix.stackexchange.com/a/542520/108618) will probably perform better: it doesn't create files; it doesn't pipe into `tail`; it doesn't spawn `cmp`; it uses `[` which is a builtin in many shells.