2

One thing that I frequently do is edit the most recently modified files, so instead of typing "ls -lr" and then "vim lastfile", I thought I would make some shortcuts in my ~/.bash_profile file:

alias via="vim `ls -rt | tail -1`"
alias vib="vim `ls -rt | tail -2 | head -1`"
alias vic="vim `ls -rt | tail -3 | head -1`"
alias vid="vim `ls -rt | tail -4 | head -1`"
alias vie="vim `ls -rt | tail -5 | head -1`"

The problem is that, weirdly enough, these commands don't work. They open some file that isn't one of the last, or even a file was deleted from the current directory (I wonder if there's some kind of file cache updating issue in the directory. This occurs on both my local machine and the cluster I work on).

However, if I type vim `ls -rt | tail -1` directly, without using the alias, it works every time.

5
  • I'll just leave the standard caveat about Parsing ls output here.
    – jw013
    Commented Nov 8, 2014 at 19:21
  • Hmm... thanks. Didn't know all that.
    – Nick
    Commented Nov 8, 2014 at 19:26
  • 1
    ...and the advice about not using backticks. Also, aliases should really go in .bashrc, not .{,bash_}profile.
    – jasonwryan
    Commented Nov 8, 2014 at 19:36
  • See, this is why I don't like Bash scripting languages. So many weird exceptions you wouldn't automatically know about.
    – Nick
    Commented Nov 8, 2014 at 21:49
  • *Also, I'm on OS X, which doesn't have a .bashrc and I believe loads .bash_profile on every login session.
    – Nick
    Commented Nov 8, 2014 at 21:50

2 Answers 2

7

The problem is you need to quote the backticks in your alias definition. Double quotes (") do not quote command substitution. You will need single quotes ('). Use

alias via='vim `ls -rt | tail -1`'

Though you'd actually want:

alias via='vim -- "$(ls -t | head -n 1)"'

That is:

  • use the modern form of command substitution ($(...)) while we are at it.
  • quote it to disable the split+glob operator (otherwise it wouldn't work properly if the file name had IFS characters or wildcards (it still doesn't work if it has newline characters)).
  • Use -- to mark the end of options for vim (otherwise, it wouldn't work for filenames starting with - or +).
  • Use ls -t | head instead of ls -rt | tail to get the result sooner.

Do not use

alias via="vim `ls -rt | tail -1`"

If you do that the command substitution happens when you define the alias, not when you run it. Try typing alias via to see that the output is not actually alias via='vim `ls -rt | tail -1`' but rather alias via='vim <prematurely expanded output>'.

0
1

With zsh (also pre-installed on OS/X) and assuming you're using the completion system (compinit and co), Ctrl+Xm becomes a completer that expands to the newest file.

So:

vi Ctrl+Xm

Would make you edit the newest file (you also get a chance to see which it before you press Return).

vi Alt+2Ctrl+Xm

For the second-newest file.

vi *.cCtrl+Xm

for the newest c file.

vi *(.)Ctrl+Xm

for the newest regular file (not directory, nor fifo/device...), and so on.

You also define your alias as:

alias via='vim -- *(.om[1])'

To edit the newest (non-hidden) regular file in the current directory without the problems involved with parsing the output of ls.

With bash and assuming the GNU implementation of find and sort, you could achieve the same with a function like:

via() {
  local file
  IFS=/ read -rd '' file file < <(
    find . -maxdepth 1 -type f  ! -name '.*' -printf '%T@/%f\0' |
    sort -rzn
  ) && vim -- "$file"
}

Which you could parameterize to be able to specify the second, third... newest file with (assuming GNU Bash 4.4 or newer and GNU coreutils 8.25 or newer):

via() {
  local files
  readarray -d '' -t files < <(
    find . -maxdepth 1 -type f  ! -name '.*' -printf '%T@/%f\0' |
    sort -rzn | cut -zd / -f 2
  ) && ((${#files[@]} >= ${1-1} )) && vim -- "${files[${1-1}-1]}"
}

To use as:

via 3

for instance to edit the third newest file.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .