Условия гонки меток времени создания файла Linux

Я пытаюсь сделать то, что я считаю простой вещью под Linux. У меня есть сценарий bash, который запускает различные тестовые программы, и я хочу определить, какие файлы в текущем каталоге были созданы тестовыми программами. Итак, я делаю что-то вроде этого:

touch timestamp-file
run the test
find -newer timestamp-file -type f > list-of-files
rm -f timestamp-file

Оказывается, степень детализации find -newer плохая, поэтому обычно происходит то, что некоторые файлы, сгенерированные тестовой программой, отображаются как СТАРЕЕ, чем файл временной метки. Итак, я попробовал это:

ls -tr1 | sed '1,/timestamp-file/d'

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

Спасибо!

P.S. Я могу сделать это другим способом, сделав два снимка каталога, один до запуска тестовой программы, а другой после, и сравнив их. Любые файлы во втором списке, которых нет в первом, должны быть созданы тестовой программой (меня не интересуют фоновые задания или другие пользователи, записывающие в каталог). Но этот метод не то, что мне нужно, потому что, если выходной файл не был удален до запуска теста (они должны быть удалены, но в некоторых случаях могут и не быть), этот метод скажет, что он не был создан тестовая программа, поскольку она находилась в каталоге до запуска тестовой программы.


person Dave Wade-Stein    schedule 02.01.2009    source источник


Ответы (4)


Возьмите имена всех файлов перед запуском, но включите их временные метки:

find -printf '%p %T@\n' | sort > file1

Если у вас нет этой опции поиска, вы также можете использовать статистику для этой работы:

find -print0 | xargs -0 stat -c "%n %Y" | sort > file1

А после пробега до file2. Затем используйте

comm -1 -3 file1 file2

И он покажет вам строки, уникальные для file2, которые должны быть новыми файлами, если я не ошибаюсь. Если они существовали раньше, время их модификации изменится, о чем позаботится %T@ штучка (распечатывая количество секунд с 1970 года):

[js@HOST2 cpp]$ find -printf '%p %T@\n' | sort > file1
[js@HOST2 cpp]$ echo foo>bar
[js@HOST2 cpp]$ echo foo>baz
[js@HOST2 cpp]$ find -printf '%p %T@\n' | sort > file2
[js@HOST2 cpp]$ comm -1 -3 file1 file2
. 1230947309.0000000000
./bar 1230947308.0000000000
./baz 1230947309.0000000000
./file2 1230947315.0000000000
[js@HOST2 cpp]$ find -printf '%p %T@\n' | sort > file1
[js@HOST2 cpp]$ echo lol>bar
[js@HOST2 cpp]$ find -printf '%p %T@\n' | sort > file2
[js@HOST2 cpp]$ comm -1 -3 file1 file2
./bar 1230947359.0000000000
./file2 1230947362.0000000000
[js@HOST2 cpp]$`
person Johannes Schaub - litb    schedule 03.01.2009
comment
Это круто. Я думал, что решение состоит в том, чтобы объединить два моих метода, но я бы использовал неуклюжий 'ls -l' и полностью забыл о comm (я никогда не был знаком с ним, но теперь я знаю). БЛАГОДАРНОСТЬ! - person Dave Wade-Stein; 03.01.2009
comment
Оказывается, find не работает таким образом в Darwin (Mac OS), поэтому я вернулся к использованию ls -l, потому что мне нужно, чтобы он работал на многих платформах, но ваше решение по-прежнему остается лучшим для меня. Спасибо! - person Dave Wade-Stein; 04.01.2009
comment
Дэйв Уэйд-Стейн, вы также можете использовать статистику вместо поиска. найти -print0 | xargs -0 стат -c %n %Y . может это в наличии? - person Johannes Schaub - litb; 04.01.2009

На самом деле вы можете использовать touch, чтобы принудительно установить временные метки всех текущих файлов в каталоге в далекое прошлое, например:

touch -t 200801010000.00 *

Если вы сделаете это перед запуском теста, разницы во времени должно быть более чем достаточно, чтобы "find -newer" забрал его. Если бы степень детализации составляла две минуты, вы могли бы установить для всех текущих файлов значение «10 минут назад», для файла временной метки — значение «5 минут назад», а затем запустить тест.

Таким образом, ваш сценарий становится:

touch -t (current time - 10 minutes) *
touch -t (current time -  5 minutes) timestamp-file
run the test
find -newer timestamp-file -type f > list-of-files
rm -f timestamp-file

Предполагая, что у вас есть приличная установка Perl, вы можете сделать следующее, чтобы получить 5 минут назад (или использовать -600 для 10 минут) в правильном формате для «date -t»:

use Date::Manip;
print UnixDate(DateCalc(ParseDateString("now"),"-300"),"%Y%m%d%H%M.%S") . "\n";

Если по какой-то причине вам не разрешено изменять метки времени, используйте:

sleep 300
touch timestamp-file
sleep 300
run the test
find -newer timestamp-file -type f > list-of-files
rm -f timestamp-file

который имеет тот же эффект, но дает вам десять минут, чтобы пойти выпить кофе (или ваш любимый яд, если вы не пьете кофе).

person paxdiablo    schedule 02.01.2009
comment
Собственно, просто поставить usleep 500000 после тача работает, но как то коряво. Весь пакет занимает много времени, поэтому добавление таких задержек проблематично. Большинство тестов занимает 10-60 секунд. для запуска, поэтому нет состояния гонки. Но тест, который занимает доли секунды, вызывает проблемы. - person Dave Wade-Stein; 03.01.2009
comment
Ваше первое решение убедительно. Нет никакой реальной проблемы с изменением временных меток, поэтому я могу установить их, как вы описали. Жаль, что touch -t не принимает секунды с 1970 года в качестве аргумента. знак равно - person Dave Wade-Stein; 03.01.2009
comment
@Dave, если у вас установлен Perl, материал Date::Manip из CPAN должен довольно легко преобразовывать временные метки UNIX в строки и обратно. - person paxdiablo; 03.01.2009

Если вы рассмотрите, как реализован find(1), станет ясно, почему это иногда может работать не так, как вы ожидаете. Вот подсказка:

  $ touch timestamp ; touch newer ; find . -newer timestamp 
  $ rm timestamp newer
  $ touch timestamp ; sleep 1 ; touch newer ; find . -newer timestamp
  .
  ./newer
  $

find(1) получает значения файла mtime/ctime/atime с помощью системного вызова stat(2). Вот элементы struct stat из <sys/stat.h> (Linux):

  time_t    st_atime;   /* time of last access */
  time_t    st_mtime;   /* time of last modification */
  time_t    st_ctime;   /* time of last status change */

В Linux (и вообще в Unix) time_t - это целое число, представляющее «секунды с начала 1970 года». Поэтому наилучшая степень детализации, которую может понять -newer, составляет всего одну секунду.

person Martin Carpenter    schedule 03.01.2009

Почему бы не создать временный каталог для запуска тестов? Используйте имя каталога на основе метки времени, чтобы отслеживать, когда и какие результаты произошли, и просто удалите весь каталог, когда закончите.

person Shannon Nelson    schedule 02.01.2009
comment
Спасибо... это разумное предложение. Проблема в том, что есть несколько ожиданий того, какие файлы будут найдены в каталоге, и они могут быть разными для каждого теста. Поэтому я априори не знаю, какие входные файлы мне нужно скопировать во временный каталог. Я подумаю об этом еще... - person Dave Wade-Stein; 03.01.2009
comment
известно, что детализация временных меток не является стандартной. Просто: mkdir tmp (cd tmp #ссылочные файлы в .. а не . ) rm -Rf tmp - person pixelbeat; 03.01.2009