Esiste un modo per rendere i collegamenti simbolici perl -i non clobber?


12

Un mio amico sottolinea che se lo fai:

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

quando "somefile" è in realtà un collegamento simbolico, perl fa esattamente ciò che i documenti dicono che farà:

Lo fa rinominando il file di input, aprendo il file di output con il nome originale e selezionando quel file di output come predefinito per le istruzioni print (). L'estensione, se fornita, viene utilizzata per modificare il nome del vecchio file per creare una copia di backup [...]

Il risultato è un nuovo link simbolico "somefile.bak" che punta al file reale invariato e un nuovo file normale "somefile" modificato con le modifiche.

In molti casi, seguire il collegamento simbolico sarebbe il comportamento desiderato (anche se lascia ambigua la posizione corretta del file .bak). Esiste un modo semplice per farlo oltre a testare i symlink in un wrapper e gestire il caso in modo appropriato?

( sedfa la stessa cosa, per quello che vale.)


1
Chiama vim o emacs (penso che entrambi seguano i symlink)? Seriamente, temo che la risposta sia reimplementazione -p -inella tua sceneggiatura.
Gilles 'SO- smetti di essere malvagio' il

In realtà, Sed non fa la stessa cosa, almeno non dalla versione 4.2.1, rilasciata nel giugno del 2009. È necessario includere l'opzione --follow-symlink per la modifica per modificare il file collegato anziché ostruire il collegamento simbolico ; Presumo che ciò sia stato fatto per evitare la rottura di script esistenti che potrebbero dipendere dal vecchio comportamento.
Garrett,

Risposte:


6

Mi chiedo se la piccola spongeutility generica (" assorbi input standard e scrivi su un file") da moreutils sarà utile in questo caso e se seguirà il link simbolico.

L'autore descrive sponge così:

Risolve il problema della modifica dei file sul posto con gli strumenti Unix, vale a dire che se si reindirizza l'output sul file che si sta tentando di modificare, il reindirizzamento ha effetto (bloccando il contenuto del file) prima del primo comando nella pipeline passa alla lettura del file. Cambia come sed -ie perl -iaggira questo, ma non tutti i comandi che potresti voler usare in una pipeline hanno una tale opzione e non puoi comunque usare questo approccio con pipeline a più comandi.

Normalmente uso la spugna un po 'come questa:

sed '...' file | grep '...' | sponge file

Ehi figo, funziona. Sospetto che il commento di Gilles sopra sia la risposta "reale", ma dal momento che non l'ha fatta come una risposta, e da quando ho imparato una nuova utilità, prenderò questa. :)
mattdm,

Ma hai testato spongeper un tale utilizzo in pratica? Quanto a me: non ancora. Potresti per favore lasciare un commento affermando se si è comportato in un test nel modo desiderato qui? Oh, vedo il commento. Grazie per la conferma!
imz - Ivan Zakharyaschev

- sì, l'ho provato prima di rispondere. Quando filenel tuo esempio sopra è un collegamento simbolico, il collegamento viene lasciato solo e il file reale viene modificato.
Mattdm,

@mattdm: Sì, grazie per la conferma! (Ho notato le tue parole "che funzionano" un po 'più tardi di quanto ho scritto il mio commento.)
imz - Ivan Zakharyaschev

3

Se sai per certo che si somefiletratta di un collegamento simbolico, puoi fornire esplicitamente l'intero percorso del file con quanto segue:

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

In questo modo, il collegamento simbolico originale rimane intatto poiché la sostituzione viene ora eseguita direttamente con il file originale.


Ma l'output di readlinkpotrebbe essere ancora un altro link simbolico. Migliore sarebbe quella di utilizzare -fo -edove disponibile o zshs' $file:Po (:P)glob qualificazione come dimostra @ikegami per ottenere un percorso libero-link simbolico.
Stéphane Chazelas,

3

Se hai a che fare con un singolo file, il comando dovrebbe apparire come segue:

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

La soluzione quindi è la seguente:

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

Gestisce sia i symlink che i non symlink.


Se hai a che fare con un numero arbitrariamente elevato di file, il comando sarà simile al seguente:

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

La soluzione quindi è la seguente:

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

Gestisce sia i symlink che i non symlink.


Caveat readlink non è un comando standard, quindi la sua presenza, i suoi argomenti e le sue funzionalità variano a seconda del sistema, quindi assicurati di controllare la tua documentazione. Ad esempio, alcune implementazioni hanno -f(che è possibile utilizzare anche qui), ma non -ealtre. busyboxsi readlink -fcomporta come GNU readlink -e. E alcuni sistemi hanno un realpathcomando invece readlinkche generalmente si comporta come GNU readlink -f.

Attenzione che con GNU readlink -e, i collegamenti simbolici che non possono essere risolti in un file esistente vengono rimossi silenziosamente.


@ Stéphane Chazelas, $var:Pnon funziona per me. ( X='tmp' zsh -c 'perl -E"say for @ARGV" $X:P'uscite tmp:P)
ikegami il

hai bisogno di zsh 5.3 (2016) o superiore per $var:P. Con le versioni precedenti, puoi sempre utilizzare $var:Ainvece. Vedere il manuale per le differenze e zsh.org/mla/users/2016/msg00593.html per le motivazioni dell'introduzione :P(l' realpath()interfaccia reale ).
Stéphane Chazelas,

:Astato aggiunto in 4.3.10 (2009), GNU readlink -znel 2012, non sono a conoscenza di altre readlinkimplementazioni che supportano -z). Nota che con GNU xargs, generalmente vuoi l' -ropzione a meno che tu non possa garantire che l'input non sarà vuoto. Qui, non è un grosso problema (a meno che il perlcodice non contenga un'istruzione BEGIN/ END), riceveresti un -i used with no filenames on the command line, reading from STDIN.avviso se ciò accade.
Stéphane Chazelas,

Oops, ho letto male -rla documentazione. Pensavo di aver fatto il contrario di quello che fa. Readded.
ikegami,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.