Comment la modification inplace d'un file est-elle effectuée?

Que signifie la modification «inplace» d'un file, par exemple via sed -i ou perl -i ?
Ma question porte sur la façon dont cette modification est effectuée. Le file est-il copié, la modification est-elle effectuée dans la copy, puis remplacée l'original? Ou est-ce que le file original est en quelque sorte modifié?

sed crée un file temporaire, écrit la sortie dans ce file, puis renomme le file temporaire au-dessus de l'original.

Vous pouvez regarder ce qui se passe en utilisant strace :

 $ strace -e trace=file sed -i -e '' a execve("/usr/bin/sed", ["sed", "-i", "-e", "", "a"], [/* 34 vars */]) = 0 <...sortingmmed...> open("a", O_RDONLY) = 3 open("./sedxvhRY8", O_RDWR|O_CREAT|O_EXCL, 0600) = 4 rename("./sedxvhRY8", "a") = 0 +++ exited with 0 +++ 

Cela enregistre toutes les opérations de file sed rend: il crée un nouveau file (en toute security avec O_CREAT|O_EXCL ), y écrit datatables, puis le recule par-dessus le file original a .

sed -i accepte un suffixe à utiliser pour une sauvegarde, et dans ce cas, il déplace l'original d'abord (plutôt que de le renommer par-dessus). Cet argument est obligatoire dans la plupart des BSD sed s. Dans ce cas, il y a un bref moment où il n'y a pas de file par le bon nom dans le directory du tout.

perl dans les versions récentes ouvre le file d'input, puis le supprime et crée un nouveau file avec le même nom:

 open("a", O_RDONLY) = 3 unlink("a") = 0 open("a", O_WRONLY|O_CREAT|O_EXCL, 0600) = 4 

Lorsque vous supprimez ( unlink ) un file déjà ouvert, vous en conservez l'access aussi longtime que vous conservez le handle, afin qu'il puisse continuer à lire datatables du file supprimé. De cette façon, perl écrit directement dans le file de sortie plutôt que dans un file temporaire: aucun file supplémentaire n'est créé, mais si vous lisez le file pendant le process, vous obtiendrez du contenu partiel, contrairement à l'approche de sed . Il y a aussi un bref moment où il n'y a pas de file avec le bon nom, qui est au début du process plutôt qu'à la fin (comme dans sed -i .bak ).


Les deux sed et perl vont:

  • Remplacer un lien symbolique par un file ordinaire.
  • Briser les liens durs.
  • Préservez la propriété du groupe si possible.
  • Créez le file avec votre groupe par défaut (ou le groupe du directory parent si ce directory contient le bit setgid ) s'il appartenait à un groupe dans lequel vous n'êtes pas et que vous n'êtes pas root.
  • Conservez la propriété du file si vous êtes root.
  • Préserver les permissions de base.
  • Préservez les bits setuid et setgrp , si le groupe résultant est le même que le groupe dans setgrp il a commencé.
  • Préserver le peu collant.
  • Ne pas conserver les xattrs.

sed va:

  • Préserver les ACL (sur Linux, je ne connais pas les autres) .

perl va:

  • Ne pas conserver les lists de contrôle d'access.

Ce qui précède est vrai sur Linux avec GNU sed et Mac OS X avec son (dérivé de FreeBSD) sed .

En plus de la réponse de @ Homer, de perldoc perlrun :

spécifie que les files traités par la construction "<>" doivent être modifiés sur place. Pour cela, renommez le file d'input, ouvrez le file de sortie par son nom d'origine et select ce file de sortie comme valeur par défaut pour les instructions print (). L'extension, si fournie, est utilisée pour modifier le nom de l'ancien file pour effectuer une copy de sauvegarde, en suivant les règles suivantes:

Si aucune extension n'est fournie, aucune sauvegarde n'est effectuée et le file en cours est écrasé.

Si l'extension ne contient pas de *, elle est ajoutée à la fin du nom de file courant en tant que suffixe. Si l'extension contient un ou plusieurs caractères *, chaque * est remplacé par le nom de file actuel.

Et callbackez-vous que, aucun lien logiciel ou lien dur n'est préservé:

Notez que parce que -i renomme ou supprime le file d'origine avant de créer un nouveau file du même nom, les liens mous et durs de type UNIX ne seront pas conservés.

Enfin, le commutateur -i n'empêche pas l'exécution quand aucun file n'est donné sur la command line. Dans ce cas, aucune sauvegarde n'est effectuée (le file d'origine ne peut évidemment pas être déterminé) et le traitement passe de STDIN à STDOUT comme prévu.

Cela explique également pourquoi vous devez utiliser -i avec l'option -p ou utiliser une instruction d' print explicite si vous souhaitez modifier inplace avec perl :

 # Opps, file will be truncated, becomes empty $ perl -i.bak -ne 's/123/qwe/' file # Right way $ perl -i.bak -ne 's/123/qwe/;print' file # Or $ perl -i.bak -pe 's/123/qwe/' file