Existe-t-il un moyen de faire des liens symboliques perl -i pas clobber?

12

Un de mes amis fait remarquer que si vous le faites:

perl -pi.bak -e 's/foo/bar/' somefile

quand "somefile" est en fait un lien symbolique, perl fait exactement ce que les docs disent qu'il fera:

Il le fait en renommant le fichier d'entrée, en ouvrant le fichier de sortie par le nom d'origine et en sélectionnant ce fichier de sortie par défaut pour les instructions print (). L'extension, si fournie, permet de modifier le nom de l'ancien fichier pour en faire une copie de sauvegarde [...]

Ce qui entraîne un nouveau lien symbolique "somefile.bak" pointant vers le fichier réel inchangé, et un nouveau fichier régulier modifié "somefile" avec les modifications.

Dans de nombreux cas, suivre le lien symbolique serait le comportement souhaité (même s'il laisse ambigu l'emplacement correct du fichier .bak). Existe-t-il un moyen simple de le faire autre que de tester les liens symboliques dans un wrapper et de gérer le cas de manière appropriée?

( sedfait la même chose, pour ce que ça vaut.)

mattdm
la source
1
Appelez vim ou emacs (je pense que les deux suivent les liens symboliques)? Sérieusement, je crains que la réponse soit à réimplémenter -p -idans votre script.
Gilles 'SO- arrête d'être méchant'
Sed ne fait en fait pas la même chose, du moins pas depuis la version 4.2.1, sortie en juin 2009. Vous devez inclure l'option --follow-symlinks pour l'édition pour éditer le fichier lié plutôt que d'altérer le lien symbolique ; Je suppose que cela a été fait pour éviter la rupture des scripts existants qui peuvent dépendre de l'ancien comportement.
Garrett

Réponses:

6

Je me demande si le petit spongeutilitaire à usage général ("absorber l'entrée standard et écrire dans un fichier") de moreutils sera utile dans ce cas et s'il suivra le lien symbolique.

L'auteur décrit sponge comme ceci:

Il résout le problème de l'édition de fichiers sur place avec les outils Unix, à savoir que si vous redirigez simplement la sortie vers le fichier que vous essayez d'éditer, la redirection prend effet (encombrant le contenu du fichier) avant la première commande du pipeline se met à lire le fichier. Les commutateurs aiment sed -iet perl -icontournent cela, mais pas toutes les commandes que vous voudrez peut-être utiliser dans un pipeline ont une telle option, et vous ne pouvez pas utiliser cette approche avec des pipelines à commandes multiples de toute façon.

J'utilise normalement une éponge un peu comme ça:

sed '...' file | grep '...' | sponge file
imz - Ivan Zakharyaschev
la source
Hé cool, ça marche. Je soupçonne que le commentaire de Gilles ci-dessus est la "vraie" réponse, mais comme il ne l'a pas fait comme réponse, et depuis que j'ai appris un nouvel utilitaire, je vais prendre celle-ci. :)
mattdm
Mais avez-vous testé spongeun tel usage dans la pratique? Quant à moi: pas encore. Pourriez-vous s'il vous plaît laisser un commentaire indiquant s'il s'est comporté dans un test de la manière souhaitée ici? Oh, je vois le commentaire. Merçi pour la confirmation!
imz - Ivan Zakharyaschev
- oui, je l'ai essayé avant de répondre. Lorsque filedans votre exemple ci-dessus est un lien symbolique, le lien est laissé seul et le vrai fichier a été modifié.
mattdm
@mattdm: Oui, merci pour la confirmation! (J'ai remarqué vos mots "ça marche" un peu plus tard que j'ai écrit mon commentaire.)
imz - Ivan Zakharyaschev
3

Si vous savez somefilepertinemment qu'il s'agit d'un lien symbolique, vous pouvez explicitement fournir le chemin d'accès complet au fichier avec les éléments suivants:

perl -pi.bak -e 's/foo/bar/' $(readlink somefile)

De cette façon, le lien symbolique d'origine reste intact puisque le remplacement est désormais effectué directement avec le fichier d'origine.

IDDQD
la source
Mais la sortie de readlinkpeut encore être un autre lien symbolique. Le mieux serait d'utiliser -fou -esi disponible ou zshle qualificatif $file:Pou (:P)glob comme indiqué par @ikegami pour obtenir un chemin sans lien symbolique.
Stéphane Chazelas
3

Si vous avez affaire à un seul fichier, la commande se présente comme suit:

perl -i -pe'...' -- "$qfn"

La solution est alors la suivante:

perl -i -pe'...' -- "$( readlink -e -- "$qfn" )"

Cela gère à la fois les liens symboliques et les liens non symboliques.


Si vous traitez avec un nombre arbitrairement élevé de fichiers, la commande se présente comme suit:

... | xargs -r perl -i -pe'...' --

La solution est alors la suivante:

... | xargs -r readlink -ze -- | xargs -r0 perl -i -pe'...' --

Cela gère à la fois les liens symboliques et les liens non symboliques.


Caveat readlink n'est pas une commande standard, donc sa présence, ses arguments et ses fonctionnalités varient selon le système, alors assurez-vous de vérifier votre documentation. Par exemple, certaines implémentations ont -f(que vous pouvez également utiliser ici), mais pas -e, d'autres non plus. busybox« s readlink -fse comporte comme GNU readlink -e. Et certains systèmes ont une realpathcommande au lieu de readlinklaquelle se comporte généralement comme GNU readlink -f.

Attention avec GNU readlink -e, les liens symboliques qui ne peuvent pas être résolus vers un fichier existant sont supprimés en silence.

ikegami
la source
@ Stéphane Chazelas, $var:Pça ne marche pas pour moi. ( X='tmp' zsh -c 'perl -E"say for @ARGV" $X:P'sorties tmp:P)
ikegami
vous avez besoin de zsh 5.3 (2016) ou supérieur pour $var:P. Avec les anciennes versions, vous pouvez toujours utiliser à la $var:Aplace. Voir le manuel pour les différences et zsh.org/mla/users/2016/msg00593.html pour la justification de l'introduction :P(l' realpath()interface réelle ).
Stéphane Chazelas
(a :Aété ajouté dans 4.3.10 (2009), GNU readlink -zen 2012, je ne connais aucune autre readlinkimplémentation qui prend en charge -z). Notez qu'avec GNU xargs, vous voulez généralement l' -roption sauf si vous pouvez garantir que l'entrée ne sera pas vide. Ici, ce n'est pas grave (sauf si le perlcode contient une instruction BEGIN/ END), vous obtiendrez simplement un -i used with no filenames on the command line, reading from STDIN.avertissement si cela se produit.
Stéphane Chazelas
Oups, j'ai mal lu -rla documentation. Je pensais avoir fait le contraire de ce qu'il fait. Readded.
ikegami