20

For files within a specific folder, I would like vim to never touch "modified" timestamp at all.

The background is that I use Bloxsom for blogging, which uses plain text files in ~/bloxsom as source of all articles. The article date (and therefore order of appearence) is based on modification date of the text file. I don't want article pop up like if it's new whenever I just fix a typo. (I do lots of them... :D)

So far, vim changes timestamp and the original stamp is lost forever. This is OK and I want to keep it that way for most of files on the system. But I don't want that for the blog files - I can always touch the file if I need.

Any ideas on how to tweak vim into this behavior?

22

I don't think vim has this feature. One alternative is to modify a copy and set timestamp appropriately, e.g.:

cp -p post temp
vim temp
touch -r post temp
cp -p temp post

Or even better:

touch -r post timestamp
vim post
touch -r timestamp post
7

If you need to have some fun:

file=path; mtime=$(stat -c %y "$file"); vi "$file"; touch -d "$mtime" "$file"

Replace path with your actual file path

5

I wrote a Perl script for that.

It's a wrapper around vim, saved in ~/bin/vim-nomtime.pl and brought into use via alias vim='~/bin/vim-nomtime.pl' in my .bashrc.

#!/usr/bin/perl
use strict;
use warnings;
use Cwd qw/ abs_path /;

my @dirs = qw( ~/blosxom /srv/relief/blosxom );

s/^~/$ENV{HOME}/ foreach @dirs;

# do we want to preserve stamp on this file?
sub fits {
    my $dir = abs_path($_[0]);
    my $fits = 0;
    foreach (@dirs) {
        my $preserved_dir = abs_path($_);
        $fits++ if $dir =~ m|^$preserved_dir|;
    }
    return $fits != 0;
}

# store original stamps
my $original_stamp;
foreach (@ARGV) {
    if ( -f $_ and fits($_) ) {
        $original_stamp->{$_} = (stat($_))[9];
    }
}

# run vim as you would
my $cmd = join ' ', 'vim', @ARGV;  
system($cmd) == 0
    or die "vim failed: $!";

# restore stamps if they have changed
foreach (keys %$original_stamp) {
    next unless -f $_;
    my $current_stamp = (stat($_))[9];
    unless ($current_stamp == $original_stamp->{$_}) {
        utime $original_stamp->{$_}, $original_stamp->{$_}, $_;
    }
}

Some good features:

  • supports multiple filenames

  • supports multiple "watched" dirs

  • supports symlinked dir

  • can be enhanced with other criteria

most of which could be probably also achieved with pure vim version. Disadvantages of this solution compared to my desired pure-vim solution are:

  • it restores stamp only after vim has quit, so if I do a long edit and save regularly, the file vill "pop-up" as new until I quit vim

  • it supports multiple files on command line, but in a quite naive way--it only checks if the thing on @ARGV is a file. This probably would not work with wildcards (e.g. vim note-*.txt) or other funny stuff

  • it's not crash proof, probably not even HUP-proof (that could be done)

  • ...wel, it's a wrapper. (I mean, if we solved everything via wrapper, how many wrappers would we have before something bad happened?)

  • May 3, 2019, and I just modified a file from Nov 11, 2017 and it has both my modification and preserves the original timestamp. :) – jmort253 May 3 at 12:08
1

This short script will preserve the modified time if any parent dir of the file contains a .nomtime file:

#!/bin/bash

dir="${1%/*}"
[ "$dir" = "$1" ] && dir=.
dir=$( readlink -f "$dir" )

nomtime=
while [ -n "$dir" ]; do
    if [ -f "$dir/.nomtime" ]; then
        nomtime=1
        break
    fi
    dir="${dir%/*}"
done

if [ "$nomtime" = 1 ]; then
    T=`tempfile`
    touch -r "$1" $T
fi

vi "$1"

if [ "$nomtime" = 1 ]; then
    touch -r $T "$1"
    rm $T
fi
1

Try this bash function (base on Shâu Shắc's answer)

vi-preserve-time () {
    for file in "$@"; do
        local mtime=$(stat -c %y "$file")
        vi "$file"
        touch -d "$mtime" "$file"
    done
}

Now we can edit file and preserve modified time using this

vi-preserve-time file1.txt file2.txt
  • (1) This is almost identical to Shâu Shắc’s answer from five years ago.   (2) This assumes that the user invokes vi on only a single file at a time; it won’t work for vi file1 file2.   (3) It goes without saying that, if the user switches to a different file with :e, it won’t be handled. – G-Man Mar 15 '18 at 1:53
  • Sorry about forget to reference, now I added a reference link to Shâu Shắc’s answer. – Steely Wing Mar 15 '18 at 3:01
  • About your question #3, I think if you open another file is not in the command line, it is your problem. – Steely Wing Mar 15 '18 at 3:04
  • 1
    My point is, if the question asks “How can I make vim behave the way I want?”, and you provide a function that looks like it is making vim behave the way the user wants, it’s your responsibility to document any limitations of your answer. OK, maybe my comment is a little unfair, insomuch as all the other answers seem to have the same limitation, and none of them mention it, as far as I can tell. I was just reviewing your answer because it was new; I didn’t spend a lot of time looking at the other answers. – G-Man Mar 15 '18 at 3:17
1

I found a great solution on this web page, which creates a vim function to save the current file while preserving the existing modification time, and binds this function to the F4 function key:

However, I found that the original function contains a minor bug, triggering the following warning if F4 is used twice on the same file, because vim gets confused when the modification time changes:

WARNING: The file has been changed since reading it!!!
Do you really want to write to it (y/n)?

Luckily, this is easy to fix: I added an "edit" command to the original function to reload the file after restoring the timestamp, so vim knows what modification time to expect the file to have.

Here is the modified vim function with this bug fix, which can be added to ~/.vimrc:

function! WriteSmall()
    let mtime = system("date -d @`stat -c %Y ".shellescape(expand('%:p')) . "`")
    write
    call system("touch --date='".mtime."' ".shellescape(expand('%:p')))
    edit
endfunction
map <F4> :call WriteSmall()<CR>

Note: This function relies on GNU versions of date, stat and touch.

0

Using some of the concepts described above, here is a very quick and dirty one-liner alias for CSH that makes an "edit" command available for this purpose:

alias edit 'touch -r \!:1 /tmp/~mtime ; vim \!:1 ; touch -r /tmp/~mtime \!:1 ; rm /tmp/~mtime'

You could easily use the $VISUAL or $EDITOR environment variable in place of "vim". This way you can issue the following command to change default editors

setenv VISUAL nano

Now the alias itself doesn't need to be redefined to accommodate different editors, making it somewhat more portable. If you use a shell other than CSH or TCSH, of course, the examples above can be adapted accordingly.

0

There is an option to copy a timestamp from another file.

touch -m -r file_source file_target

Example:

[oracle@es abc]$ ls -tlr
total 4
-rw-r--r--. 1 oracle oinstall 102 May  8 20:35 aa.txt
[oracle@es abc]$ echo "hi hi" > b
[oracle@es abc]$ ls -ltr
total 4
-rw-r--r--. 1 oracle oinstall 102 May  8 20:35 aa.txt
-rw-r--r--. 1 oracle oinstall   6 May 13 18:58 b
[oracle@es abc]$ touch -m -r aa.txt b
[oracle@es abc]$ ls -tlr
total 8
-rw-r--r--. 1 oracle oinstall   6 May  8 20:35 b
-rw-r--r--. 1 oracle oinstall 102 May  8 20:35 aa.txt
[oracle@es abc]$ cat b
hi hi

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.