Виконання тесту -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.