Виконання тесту -nt / -ot в POSIX sh


11

Вбудовані testта [утиліти мають тести -nt("новіші за") та -ot("старіші за") у більшості оболонок, навіть коли оболонка працює в режимі POSIX (також справедливо для зовнішніх утиліт з однойменними назвами на системи, до яких я маю доступ). Ці тести для порівняння часових позначок модифікації на два файли. Їх задокументована семантика дещо відрізняється в різних реалізаціях (що стосується того, що відбувається, якщо той чи інший файл існує не), але вони не включені до специфікації POSIX. за testутиліту .

Вони не були перенесені в testутиліту, коли умовна команда була вилучена з оболонки [KornShell], оскільки вони не були включені в testутиліту, вбудовану в історичні реалізації цієї shутиліти.

Припускаючи, що я хотів би порівняти часову позначку модифікації між файлами в /bin/shсценарії оболонки, а потім вжити заходів залежно від того, чи є один файл новішим, ніж інший, як у

if [ "$sigfile"     -nt "$timestamp" ] ||
   [ "$sigfile.tmp" -nt "$timestamp" ]
then
    return
fi

... якою іншою утилітою я міг би скористатися, окрім make(що зробило б решту сценарію найменш складною)? Або я просто припускаю, що ніхто ніколи не збирається запускати сценарій про "історичну реалізацію sh", або відмовлятися від написання для конкретної оболонки, як bash?


Як ви бачите у моїй відповіді, bash не реалізує -ntфункцію testтаким чином, який очікується приблизно з ок. 1995. Ви повинні використовувати findвираз basd, навіть bashякщо вам подобається правильна поведінка.
шилі

Відповіді:


10

ПОСІБНО:

f1=/path/to/file_1
f2=/path/to/file_2

if [ -n "$(find -L "$f1" -prune -newer "$f2")" ]; then
    printf '%s is newer than %s\n' "$f1" "$f2"
fi

Використання абсолютного шляху до файлів запобігає хибному позитиву, а ім'я файлу містить лише нові рядки.

У разі використання відносного шляху, тоді змініть findкоманду на:

find -L "$f1" -prune -newer "$f2" -exec echo . \;

технічно я думаю, що це не вдається, якщо $f1містить лише нові рядки;)
ilkkachu

1
@ilkkachu можна було б обробити з -exec echo x \;чи подібним?
муру

@muru, так. -printfбуло б легко, якби це було стандартно
ilkkachu

3
@Kusalananda AFAICT, findстатус виходу незалежно від того, які індивідуальні тести findповернення - за винятком, можливо, якщо в цих тестах є помилка. findвиходить з 0, навіть якщо файлів не знайдено.
муру

1
Зауважте, що поведінка [ / -nt /nofile ]змінюється залежно від реалізації (але ніколи не видає помилок).
Стефан Шазелас

7

Це може бути випадок , для використання однієї з найстаріших команд Unix, ls.

x=$(ls -tdL -- "$a" "$b")
[ "$x" = "$a
$b" ]

Результат вірний, якщо a є новішим за b.


3
Це не працює, якщо імена файлів починаються з -(відсутні --). Вам потрібно, -Lщоб це було рівнозначно -nt. Це не працює для порівняння xі $'x\nx', наприклад.
Стефан Шазелас

1
Еек! Але також так.
Кусалаланда

4

Ви поставили цікаве запитання і зробили претензію, яку слід спочатку перевірити.

Я перевірив поведінку:

$shell -c '[ Makefile -nt SCCS/s.Makefile ] && echo newer'

з різними оболонками. Ось результати:

  • bash Не працює - нічого не друкує.

  • бош працює

  • тире Не працює - нічого не друкує.

  • ksh88 Не працює - нічого не друкує.

  • ksh93 працює

  • mksh Не працює - нічого не друкує.

  • posh print: posh: [: -nt: несподіваний оператор / операнд

  • яш працює

  • zsh працює у нових версіях , старіші версії нічого не друкують

Так чотири з дев'яти оболонок підтримують функцію -nt і правильно її реалізують. Правильно в цьому випадку означає: здатний порівнювати позначки часу на останніх платформах, які підтримують деталізацію часової марки під секунди . Зауважте, що вибрані мною файли відрізняються, як правило, лише декількома мікросекундами у своїх часових марках.

Оскільки легше знайти робочу findреалізацію, рекомендую замінити

if [ "$file1" -nt "$file2" ] ; then
    echo newer
fi

за findвиразом на основі.

if [ "$( find "$file1" -newer "$file2" )" ]; then
    echo newer
fi

працює принаймні до тих пір, $file1поки не містить лише нових рядків.

if [ "$( find -L "$file1" -newer "$file2" -exec echo newer \; )" ]; then
    echo newer
fi

трохи повільніше, але працює правильно.

BTW: Що стосується складання make, я не можу говорити про всі реалізації версій, але SunPro Makeпідтримує порівняння часу з наносекундною деталізацією приблизно з прибл. 20 років, хоча smakeі gmakeцю функцію додали останнім часом.


1
Чи не працюють "непрацюючі" тести через те, що не вдалося порівняти часові позначки другої секунди (безумовно?) Чи щось інше? Які фактичні часові позначки на файли, що беруть участь у вашому тесті? +1 для тестування!
Кусалаланда

2
Я думаю, що голосування, мабуть, стосується конфронтаційного формулювання. Тут було б справедливішим назвати це обмеженням (суттєвим у деяких контекстах). Деякі findреалізації, такі як зайнятий ящик або heirloom-інструмент, матимуть однакові обмеження.
Стефан Шазелас

3
Вам потрібно, -Lщоб findверсія була еквівалентною -nt. Також не вдасться назвати файли, які починаються з -або !, (...
Stéphane Chazelas

2
Власне кажучи, я часто отримую низькі коментарі, коли використовую конфронтаційне формулювання. Тут допоможе, якщо ви вказали контекст (файли, змінені протягом однієї часової позначки, коли усічені до другої роздільної здатності) на початку відповіді.
Стефан Шазелас

1
@schily, так, BSD findє find -f "$file"для цього, але це не портативно.
Стефан Шазелас
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.