Exécution du test -nt / -ot dans un POSIX sh

11

Le intégré testet les [utilitaires ont les tests -nt("plus récent que") et -ot("plus ancien que") dans la plupart des shells, même lorsque le shell s'exécute en "mode POSIX" (également vrai pour les utilitaires externes des mêmes noms sur le systèmes auxquels j'ai accès). Ces tests permettent de comparer les horodatages de modification sur deux fichiers. Leur sémantique documentée varie légèrement d'une implémentation à l'autre (en ce qui concerne ce qui se passe si l'un ou l'autre fichier n'existe pas), mais ils ne sont pas inclus dans la spécification POSIX. pour l' testutilitaire .

Ils n'ont pas été reportés dans l' testutilitaire lorsque la commande conditionnelle a été supprimée du shell [KornShell] car ils n'ont pas été inclus dans l' testutilitaire intégré aux implémentations historiques de l' shutilitaire.

En supposant que je voudrais comparer l'horodatage de modification entre les fichiers dans un /bin/shscript shell et prendre des mesures selon que l' un fichier est plus récent que l'autre, comme dans

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

... quel autre utilitaire pourrais - je utiliser, à part make( ce qui rendrait le reste du script trop compliqué pour le moins)? Ou devrais-je simplement supposer que personne ne va jamais exécuter le script sur une "implémentation historique de sh", ou se résigner à écrire pour un shell spécifique comme bash?

Kusalananda
la source
Comme vous pouvez le voir dans ma réponse, bash n'implémente pas la -ntfonctionnalité de testmanière attendue depuis env. 1995. Vous devriez utiliser l' findexpression basd même bashsi vous aimez un comportement correct.
schily

Réponses:

10

POSIX:

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

L'utilisation d'un chemin absolu vers les fichiers empêche un faux positif dont le nom de fichier contient uniquement des retours à la ligne.

En cas d'utilisation d'un chemin relatif, changez la findcommande en:

find -L "$f1" -prune -newer "$f2" -exec echo . \;
cuonglm
la source
techniquement, je pense qu'il échoue s'il $f1ne contient que des nouvelles lignes;)
ilkkachu
1
@ilkkachu pourrait être travaillé avec -exec echo x \;ou similaire?
muru
@muru, ouais. -printfserait facile s'il était standard
ilkkachu
3
Le statut de sortie de @Kusalananda AFAICT, findest indépendant de ce que les tests de findretour individuels - sauf peut-être s'il y a une erreur dans l'exécution de ces tests. findquitte 0 même si aucun fichier n'est trouvé.
muru
1
Notez que le comportement de [ / -nt /nofile ]varie selon l'implémentation (mais ne génère jamais d'erreur).
Stéphane Chazelas
7

Cela pourrait être un cas pour l' utilisation de l' un des plus anciens commande Unix, ls.

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

Le résultat est vrai si a est plus récent que b.

meuh
la source
3
Cela ne fonctionne pas si les noms de fichiers commencent par -(manquant --). Vous auriez besoin -Lque ce soit équivalent à -nt. Cela ne fonctionne pas pour comparer xet $'x\nx'par exemple.
Stéphane Chazelas
1
Eek! Mais aussi hein.
Kusalananda
4

Vous avez soulevé une question intéressante et fait une affirmation qui devrait d'abord être vérifiée.

J'ai vérifié le comportement de:

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

avec différentes coquilles. Voici les résultats:

  • bash Ne fonctionne pas - n'imprime rien.

  • bosh fonctionne

  • tiret ne fonctionne pas - n'imprime rien.

  • ksh88 Ne fonctionne pas - n'imprime rien.

  • ksh93 fonctionne

  • mksh Ne fonctionne pas - n'imprime rien.

  • imprime chic : posh: [: -nt: opérateur / opérande inattendu

  • yash fonctionne

  • zsh fonctionne dans les versions plus récentes , les versions plus anciennes n'impriment rien

Ainsi, quatre des neuf shells prennent en charge la fonction -nt et l'implémentent correctement. Dans ce cas, correctement signifie: est capable de comparer les horodatages sur les plates-formes récentes qui prennent en charge la granularité d'horodatage en moins d'une seconde . Notez que les fichiers que j'ai sélectionnés ne diffèrent généralement que de quelques microsecondes dans leurs horodatages.

Puisqu'il est plus facile de trouver une findimplémentation fonctionnelle , je recommande de remplacer

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

par une findexpression basée.

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

fonctionne au moins tant qu'il $file1ne contient pas seulement des retours à la ligne.

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

est un peu plus lent mais fonctionne correctement.

BTW: En ce qui concerne make, je ne peux pas parler pour toutes les implémentations de make, mais SunPro Makeprend en charge la comparaison du temps avec la granularité nanoseconde depuis env. 20 ans, smakeet a gmakerécemment ajouté cette fonctionnalité.

schily
la source
1
Les tests "ne fonctionnent pas" ne fonctionnent-ils pas en raison d'un échec de comparaison des horodatages de moins d'une seconde (définitivement?), Ou autre chose? Quels sont les horodatages réels des fichiers impliqués dans votre test? +1 pour le test!
Kusalananda
2
Je pense que le downvote est probablement à propos de la formulation conflictuelle. Ici, il serait plus juste de l'appeler une limitation (significative dans certains contextes). Certaines findimplémentations telles que busybox ou heirloom-toolchest auront la même limitation.
Stéphane Chazelas
3
Vous auriez besoin -Lque la findversion soit équivalente à -nt. Il échouerait également sur les noms de fichiers commençant par -ou !, (...
Stéphane Chazelas
2
En fait, je reçois souvent des downvotes lorsque j'utilise un libellé de confrontation. Ici, il serait utile que vous indiquiez le contexte (fichiers modifiés dans le même horodatage lorsqu'ils sont tronqués à la deuxième résolution) au début de la réponse.
Stéphane Chazelas
1
@schily, oui, les BSD findont find -f "$file"ça mais ce n'est pas portable.
Stéphane Chazelas