Question La redirection avec `>>` est-elle équivalente à `>` quand le fichier cible n'existe pas encore?


Considérons un shell comme Bash ou sh. La différence fondamentale entre > et >> se manifeste dans un cas où le fichier cible existe:

  • > tronque le fichier à la taille zéro, puis écrit;
  • >> ne pas tronquer, il écrit (ajoute) à la fin du fichier.

Si le fichier n'existe pas, il est créé avec une taille nulle. puis écrit à. Cela est vrai pour les deux opérateurs. Il peut sembler que les opérateurs sont équivalents lorsque le fichier cible n'existe pas encore.

Sont-ils vraiment?


78
2017-07-23 08:42


origine




Réponses:


tl; dr

Non. >> est essentiellement "toujours chercher à la fin du fichier" tout en > maintient un pointeur sur le dernier emplacement écrit.


Réponse complète

(Remarque: tous mes tests ont été effectués sur Debian GNU / Linux 9).

Une autre différence

Non, ils ne sont pas équivalents. Il y a un autre différence. Il peut se manifester indépendamment du fait que le fichier cible ait existé avant ou non.

Pour l'observer, exécutez un processus qui génère des données et redirige vers un fichier avec > ou >> (par exemple. pv -L 10k /dev/urandom > blob). Laissez-le s'exécuter et modifiez la taille du fichier (par exemple, avec truncate). Vous verrez que > maintient son décalage (croissant) tout en >> ajoute toujours à la fin.

  • Si vous tronquez le fichier à une taille inférieure (il peut être de taille zéro)
    • > ne se souciera pas, il écrira à son décalage désiré comme si rien ne s'est passé; juste après la troncature de l'offset au-delà de la fin du fichier, le fichier retrouvera son ancienne taille et augmentera encore, les données manquantes seront remplies de zéros (si possible de manière fragmentée);
    • >> va ajouter à la nouvelle fin, le fichier grandira à partir de sa taille tronquée.
  • Si vous agrandissez le fichier
    • > ne se souciera pas, il écrira à son décalage désiré comme si rien ne s'est passé; juste après avoir changé la taille, le décalage est quelque part dans le fichier, cela provoquera un arrêt du fichier pendant un certain temps, jusqu'à ce que le décalage atteigne la nouvelle extrémité, le fichier grandira normalement;
    • >> va ajouter à la nouvelle fin, le fichier grandira à partir de sa taille élargie.

Un autre exemple consiste à ajouter (avec un >>) quelque chose de plus lorsque le processus de génération de données est en cours d'exécution et écrit dans le fichier. Ceci est similaire à l'agrandissement du fichier.

  • Le processus de génération avec > va écrire à son décalage désiré et écraser les données supplémentaires éventuellement.
  • Le processus de génération avec >> ignorera les nouvelles données et les ajoutera (la condition de concurrence peut se produire, les deux flux peuvent être entrelacés, aucune donnée ne doit être écrasée).

Exemple

Est-ce important dans la pratique? Il y a cette question:

Je lance un processus qui produit beaucoup de sortie sur stdout. Tout envoyer dans un fichier [...] Puis-je utiliser un programme de rotation des journaux?

Cette réponse dit que la solution est logrotate avec copytruncate option qui agit comme ceci:

Tronquer le fichier journal d'origine en place après avoir créé une copie, au lieu de déplacer l'ancien fichier journal et éventuellement d'en créer un nouveau.

Selon ce que j'ai écrit ci-dessus, rediriger avec > rendra le journal tronqué volumineux en un rien de temps. La rareté sauvera la journée, aucun espace disque important ne devrait être gaspillé. Néanmoins, chaque journal consécutif aura de plus en plus de zéros en tête qui sont complètement inutiles.

Mais si logrotate crée des copies sans préserver la densité, ces zéros ont besoin de plus d’espace disque à chaque copie. Je n'ai pas étudié le comportement de l'outil, il peut être assez intelligent avec une faible densité ou une compression à la volée (si la compression est activée). Cependant, les zéros ne peuvent que causer des problèmes ou être neutres au mieux. rien de bon en eux.

Dans ce cas, en utilisant >> au lieu de > est nettement meilleur, même si le fichier cible est sur le point d'être créé.


Performance

Comme on peut le voir, les deux opérateurs agissent différemment non seulement au début mais aussi plus tard. Cela peut entraîner une différence de performance (subtile?). Pour l'instant, je n'ai pas de résultats de test significatifs à prendre en charge ou à réfuter, mais je pense que vous ne devriez pas assumer automatiquement que leurs performances sont les mêmes en général.


105
2017-07-23 08:42



Alors >> est essentiellement "toujours chercher à la fin du fichier" tout en > maintient un pointeur sur le dernier emplacement écrit. Il semble qu'il y ait une différence de performance subtile dans la façon dont ils fonctionnent aussi ... - Mokubai♦
Au niveau de l'appel système, >> utilise le O_APPEND drapeau à open(). Et en fait, > les usages O_TRUNC, tandis que >> pas. La combinaison de O_TRUNC | O_APPEND serait également possible, le langage shell ne fournit tout simplement pas cette fonctionnalité. - ilkkachu
@jjmontes, la source standard serait POSIX: pubs.opengroup.org/onlinepubs/9699919799.2018edition/utilities/ mais bien sûr, le manuel de Bash contient également des descriptions sur les opérateurs de redirection, y compris les opérateurs non standard pris en charge: gnu.org/software/bash/manual/html_node/Redirections.html - ilkkachu
@ilkkachu J'ai trouvé cela intéressant, car il explique les détails sur O_APPEND dont je me demandais après votre commentaire :): stackoverflow.com/questions/1154446/... - jjmontes
@Mokubai, tout système d'exploitation sain aura la longueur de fichier à portée de main quand il sera ouvert, et vérifier un drapeau et déplacer le décalage à la fin devrait disparaître dans toutes les autres écritures. Essayer d'imiter O_APPEND avec un lseek() avant chaque write() serait différent cependant, il y aurait le surcroît d'appel système supplémentaire. (Et bien sûr, cela ne fonctionnerait pas, puisqu'un autre processus pourrait write() entre.) - ilkkachu