Gibt es eine Möglichkeit, perl -i nicht zum Clobbern von Symlinks zu machen?

12

Ein Freund von mir weist darauf hin, dass, wenn Sie dies tun:

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

Wenn "somefile" tatsächlich ein Symlink ist, macht Perl genau das, was die Dokumentation sagt:

Dazu wird die Eingabedatei umbenannt, die Ausgabedatei nach dem ursprünglichen Namen geöffnet und diese Ausgabedatei als Standard für print () -Anweisungen ausgewählt. Die Erweiterung, falls angegeben, wird verwendet, um den Namen der alten Datei zu ändern und eine Sicherungskopie zu erstellen [...]

Was dazu führt, dass ein neuer Symlink "somefile.bak" auf die unveränderte reale Datei verweist und eine neue, geänderte reguläre Datei "somefile" mit den Änderungen.

In vielen Fällen ist das Verfolgen des Symlinks das gewünschte Verhalten (auch wenn der korrekte Speicherort der .bak-Datei nicht eindeutig ist). Gibt es eine einfache Möglichkeit, dies zu tun, als in einem Wrapper nach Symlinks zu suchen und den Fall angemessen zu behandeln?

( sedTut das Gleiche, für das, was es wert ist.)

mattdm
quelle
1
Vim oder emacs anrufen (ich denke beide folgen symlinks)? Im Ernst, ich fürchte, die Antwort ist, -p -iIhr Skript erneut zu implementieren .
Gilles 'SO - hör auf böse zu sein'
Tatsächlich macht Sed nicht dasselbe, zumindest nicht seit Version 4.2.1, die im Juni 2009 veröffentlicht wurde. Sie müssen die Option --follow-symlinks für die Bearbeitung einschließen, um die verknüpfte Datei zu bearbeiten, anstatt den Symlink zu löschen ; Ich gehe davon aus, dass dies getan wurde, um zu verhindern, dass vorhandene Skripte beschädigt werden, was möglicherweise vom alten Verhalten abhängt.
Garrett

Antworten:

6

Ich frage mich, ob das kleine spongeAllzweck-Dienstprogramm ("Standardeingabe aufsaugen und in eine Datei schreiben") von moreutils in diesem Fall hilfreich ist und ob es dem Symlink folgt.

Der Autor beschreibt das sponge so:

Es behebt das Problem des direkten Bearbeitens von Dateien mit Unix-Tools, dh, wenn Sie nur die Ausgabe in die Datei umleiten, die Sie bearbeiten möchten, wird die Umleitung vor dem ersten Befehl in der Pipeline wirksam kommt zum Lesen aus der Datei. Schalter wie sed -iund umgehen perl -idies, aber nicht jeder Befehl, den Sie in einer Pipeline verwenden möchten, verfügt über eine solche Option, und Sie können diesen Ansatz ohnehin nicht für Pipelines mit mehreren Befehlen verwenden.

Normalerweise benutze ich einen Schwamm wie diesen:

sed '...' file | grep '...' | sponge file
imz - Ivan Zakharyaschev
quelle
Hey cool, das funktioniert. Ich vermute, Gilles 'obiger Kommentar ist die "echte" Antwort, aber da er es nicht als Antwort gemacht hat und ich ein neues Hilfsprogramm gelernt habe, nehme ich diese. :)
Mattdm
Aber haben Sie spongeeine solche Anwendung in der Praxis getestet ? Was mich betrifft: noch nicht. Könnten Sie bitte einen Kommentar hinterlassen, der besagt, ob es sich bei einem Test so verhalten hat, wie es hier gewünscht wird? Oh, ich sehe den Kommentar. Danke für die Bestätigung!
imz - Ivan Zakharyaschev
- Ja, ich habe es versucht, bevor ich antwortete. Wenn es sich filein Ihrem obigen Beispiel um einen Symlink handelt, wird der Link alleine gelassen und die reale Datei geändert.
Mattdm
@mattdm: Ja, danke für die Bestätigung! (Ich habe Ihre Worte "das funktioniert" etwas später bemerkt, als ich meinen Kommentar geschrieben habe.)
imz - Ivan Zakharyaschev
3

Wenn Sie wissen, dass somefilees sich um einen Symlink handelt, können Sie den vollständigen Pfad zur Datei wie folgt angeben:

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

Auf diese Weise bleibt der ursprüngliche Symlink erhalten, da die Ersetzung jetzt direkt mit der ursprünglichen Datei erfolgt.

IDDQD
quelle
Aber die Ausgabe von readlinkkann immer noch ein anderer Symlink sein. Am besten wäre es zu verwenden , -foder -esofern verfügbar , oder zsh‚s $file:Poder (:P)glob Qualifier wie @ikegami gezeigt einen Symlink-freien Weg zu bekommen.
Stéphane Chazelas
3

Wenn es sich um eine einzelne Datei handelt, sieht der Befehl folgendermaßen aus:

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

Die Lösung lautet dann:

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

Dies behandelt sowohl Symlinks als auch Nicht-Symlinks.


Wenn Sie mit einer beliebig großen Anzahl von Dateien arbeiten, sieht der Befehl folgendermaßen aus:

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

Die Lösung lautet dann:

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

Dies behandelt sowohl Symlinks als auch Nicht-Symlinks.


Warnung readlink ist kein Standardbefehl, daher variieren das Vorhandensein, die Argumente und die Funktionalität des Befehls je nach System. Überprüfen Sie daher unbedingt Ihre Dokumentation. Zum Beispiel haben einige Implementierungen -f(die Sie auch hier verwenden könnten), aber nicht -e, einige auch nicht. busybox‚s readlink -fverhält sich wie GNU readlink -e. Und einige Systeme haben einen realpathBefehl, readlinkder sich im Allgemeinen wie GNU verhält readlink -f.

Beachten Sie, dass mit GNU readlink -eSymlinks, die nicht in eine vorhandene Datei aufgelöst werden können, stillschweigend entfernt werden.

Ikegami
quelle
@ Stéphane Chazelas, $var:Pfunktioniert bei mir nicht. ( X='tmp' zsh -c 'perl -E"say for @ARGV" $X:P'Ausgänge tmp:P)
Ikegami
Sie benötigen zsh 5.3 (2016) oder höher für $var:P. Bei älteren Versionen können Sie immer $var:Astattdessen verwenden. Siehe das Handbuch für die Unterschiede und zsh.org/mla/users/2016/msg00593.html als Begründung für die Einführung :P(die echte realpath()Schnittstelle).
Stéphane Chazelas
( :AWurde in 4.3.10 (2009) hinzugefügt, GNU readlink -zin 2012, mir ist keine andere readlinkImplementierung bekannt, die dies unterstützt. -z) Beachten Sie, dass Sie bei GNU xargsim Allgemeinen die -rOption wünschen, es sei denn, Sie können garantieren, dass die Eingabe nicht leer ist. Hier ist es keine große Sache (es sei denn, der perlCode enthält eine BEGIN/ END-Anweisung), Sie würden nur eine -i used with no filenames on the command line, reading from STDIN.Warnung erhalten, wenn dies passiert.
Stéphane Chazelas
Hoppla, ich habe -rdie Dokumentation falsch gelesen . Ich dachte, ich hätte das Gegenteil von dem getan, was es tut. Readded.
Ikegami