Сохранить изменённую временную метку после редактирования

Вопрос или проблема

Для файлов в определенной папке, я бы хотел, чтобы vim вообще не трогал Метку “изменён” (modified).

Дело в том, что я использую Bloxsom для ведения блога, который использует обычные текстовые файлы в ~/bloxsom как источник всех статей. Дата статьи (и, следовательно, порядок появления) основана на дате изменения текстового файла. Я не хочу, чтобы статья появлялась как будто она новая, всякий раз, когда я просто исправляю опечатку. (Я делаю много таких исправлений… :D)

До сих пор vim изменяет метку времени, и оригинальная метка потеряна навсегда. Это нормально, и я хочу, чтобы было так для большинства файлов в системе. Но для файлов блога я этого не хочу – я всегда могу использовать touch, если это необходимо.

Есть ли идеи, как настроить vim на такое поведение?

Я не думаю, что у vim есть такая функция. Одной из альтернатив является изменение копии файла и установка метки времени соответствующим образом, например:

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

Или еще лучше:

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

Если хотите повеселиться:

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

Замените path на фактический путь к вашему файлу

Я нашел отличное решение на этом веб-странице, которое создает функцию vim для сохранения текущего файла, сохраняя существующее время изменения, и привязывает эту функцию к клавише функции F4:

Тем не менее, я обнаружил, что оригинальная функция содержит небольшую ошибку, вызывая следующее предупреждение, если F4 используется дважды для одного и того же файла, так как vim запутывается, когда время изменения меняется:

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

К счастью, это легко исправить: я добавил команду “edit” в оригинальную функцию, чтобы перезагрузить файл после восстановления временной метки, чтобы vim знал, какое время изменения ожидать у файла.

Вот модифицированная функция vim с этим исправлением ошибок, которую можно добавить в ~/.vimrc:

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

Примечание: Эта функция зависит от версий date, stat и touch из GNU.

Я написал для этого perl-скрипт.

Это обертка вокруг vim, сохраненная в ~/bin/vim-nomtime.pl и используемая через alias vim='~/bin/vim-nomtime.pl' в моем .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;

# хотим ли мы сохранить метку на этом файле?
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;
}

# сохранить оригинальные метки
my $original_stamp;
foreach (@ARGV) {
    if ( -f $_ and fits($_) ) {
        $original_stamp->{$_} = (stat($_))[9];
    }
}

# запустить vim как обычно
my $cmd = join ' ', 'vim', @ARGV;  
system($cmd) == 0
    or die "vim не удался: $!";

# восстановить метки, если они изменились
foreach (keys %$original_stamp) {
    next unless -f $_;
    my $current_stamp = (stat($_))[9];
    unless ($current_stamp == $original_stamp->{$_}) {
        utime $original_stamp->{$_}, $original_stamp->{$_}, $_;
    }
}

Некоторые хорошие функции:

  • поддерживает несколько имен файлов

  • поддерживает несколько “наблюдаемых” директорий

  • поддерживает символические ссылки

  • можно улучшить другими критериями

большинство из которых, вероятно, также можно было бы достичь с помощью чистой версии vim. Недостатки этого решения по сравнению с желаемым чисто-vim решением:

  • оно восстанавливает метку только после выхода из vim, так что если я долго редактирую и регулярно сохраняю, файл будет “всплывать” как новый, пока я не выйду из vim

  • оно поддерживает несколько файлов в командной строке, но довольно наивным образом — оно просто проверяет, является ли то, что в @ARGV, файлом. Это, вероятно, не сработает с подстановочными знаками (например, vim note-*.txt) или другими странными вещами

  • оно не защищено от сбоев, вероятно, даже не защищено от HUP (это можно исправить)

  • … ну, это обертка. (Я имею в виду, насколько много оберток у нас будет, прежде чем что-то пойдет не так?)

Попробуйте эту bash функцию (основанную на ответе Шау Шакка)

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

Теперь мы можем редактировать файл и сохранять время изменения, используя это

vi-preserve-time file1.txt file2.txt

Этот короткий скрипт будет сохранять время изменения, если любая родительская директория файла содержит файл .nomtime:

#!/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

Есть опция для копирования метки времени из другого файла.

touch -m -r file_source file_target

Пример:

[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

Используя некоторые из описанных выше концепций, вот очень быстрый и грубый алиас для CSH, который делает команду “edit” доступной для этой цели:

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

Вы можете легко использовать переменные окружения $VISUAL или $EDITOR вместо “vim”. Таким образом, вы можете выполнить следующую команду, чтобы изменить редактор по умолчанию

setenv VISUAL nano

Теперь алиас сам не нуждается в переопределении для поддержки различных редакторов, что делает его более портативным. Если вы используете оболочку, отличную от CSH или TCSH, конечно, приведенные выше примеры можно адаптировать соответственно.

попробуйте :e | w

e = обновляет временную метку буфера на диске
w = записывает буфер обратно

Ответ или решение

Для сохранения метки времени изменения файла при редактировании в Vim, можно воспользоваться несколькими подходами, которые позволят избежать нежелательной модификации времени последнего изменения. Ниже приведены некоторые эффективные варианты решения этой проблемы.

1. Использование пользовательской функции в Vim

Можно создать функцию в Vim, которая будет сохранять время модификации файла и восстанавливать его после сохранения:

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

Добавив этот код в файл ~/.vimrc, вы сможете использовать клавишу F4 для сохранения файла с сохранением оригинальной метки времени. Этот метод работает при условии, что у вас установлены GNU утилиты date, stat и touch.

2. Обертка скрипта на Perl для сохранения метки времени

Создайте простой Perl-скрипт, который будет оборачивать вызов Vim и сохранять метку времени:

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

my @dirs = qw( ~/bloxsom );

foreach my $file (@ARGV) {
    if (-f $file && fits($file)) {
        my $mtime = (stat($file))[9];
        system("vim", $file);
        utime $mtime, $mtime, $file;
    }
}

sub fits {
    my $dir = abs_path($_[0]);
    foreach my $watched_dir (@dirs) {
        return 1 if $dir =~ m|^$watched_dir|;
    }
    return 0;
}

Сохраните этот скрипт в ~/bin/vim-nomtime.pl и создайте алиас в ~/.bashrc:

alias vim='~/bin/vim-nomtime.pl'

Теперь при редактировании файлов в указанных каталогах, метка времени изменения будет сохранена.

3. Использование bash-функции

Можно также создать bash-функцию, чтобы выполнять ту же задачу:

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

Теперь вы можете редактировать файлы с помощью этой функции:

vi-preserve-time file1.txt file2.txt

4. Использование .nomtime файла

Другой интересный способ — это определение наличия файла .nomtime в родительских директориях:

#!/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=$(mktemp)
    touch -r "$1" "$T"
fi

vi "$1"

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

Заключение

Каждый из вышеописанных подходов позволяет вам редактировать файлы в Vim, не изменяя их время последнего изменения. Выберите подходящий метод в зависимости от ваших требований и удобств. Некоторые пользователи предпочитают функции внутри Vim, в то время как другие могут найти удобней использовать скрипты оболочки или Perl.

Оцените материал
Добавить комментарий

Капча загружается...