¿Hay alguna manera de hacer perl -i no clobber symlinks?

12

Un amigo mío señala que si haces:

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

cuando "somefile" es en realidad un enlace simbólico, perl hace exactamente lo que dicen los documentos:

Para ello, renombra el archivo de entrada, abre el archivo de salida con el nombre original y selecciona ese archivo de salida como predeterminado para las declaraciones print (). La extensión, si se proporciona, se utiliza para modificar el nombre del archivo antiguo para hacer una copia de seguridad [...]

Lo que da como resultado un nuevo enlace simbólico "somefile.bak" que apunta al archivo real sin cambios, y un nuevo archivo regular "somefile" con los cambios.

En muchos casos, seguir el enlace simbólico sería el comportamiento deseado (incluso si deja ambigua la ubicación correcta del archivo .bak). ¿Hay una manera simple de hacer esto que no sea probar los enlaces simbólicos en un contenedor y manejar el caso adecuadamente?

( sedhace lo mismo, por lo que vale).

mattdm
fuente
1
¿Llamar a vim o emacs (creo que ambos siguen enlaces simbólicos)? En serio, me temo que la respuesta es reimplementar -p -ien tu guión.
Gilles 'SO- deja de ser malvado'
De hecho, Sed no hace lo mismo, al menos no desde la versión 4.2.1, lanzada en junio de 2009. Debe incluir la opción --follow-symlinks para que la edición edite el archivo vinculado en lugar de bloquear el enlace simbólico ; Supongo que esto se hizo para evitar la ruptura de los scripts existentes que pueden depender del comportamiento anterior.
Garrett

Respuestas:

6

Me pregunto si la pequeña spongeutilidad de propósito general ("absorber la entrada estándar y escribir en un archivo") de moreutils será útil en este caso y si seguirá el enlace simbólico.

El autor describe sponge así:

Aborda el problema de editar archivos en el lugar con las herramientas de Unix, es decir, si solo redirige la salida al archivo que está tratando de editar, la redirección surte efecto (bloqueando el contenido del archivo) antes del primer comando en la tubería se pone a leer el archivo. A los conmutadores les gusta sed -iy perl -ievitan esto, pero no todos los comandos que desee usar en una tubería tienen esa opción, y de todos modos no puede usar ese enfoque con tuberías de comandos múltiples.

Normalmente uso una esponja como esta:

sed '...' file | grep '...' | sponge file
imz - Ivan Zakharyaschev
fuente
Hola genial, eso funciona. Sospecho que el comentario de Gilles anterior es la respuesta "real", pero como no lo hizo como respuesta, y como aprendí una nueva utilidad, tomaré esta. :)
mattdm
¿Pero has probado spongepara tal uso en la práctica? En cuanto a mí: todavía no. ¿Podría por favor dejar un comentario que indique si se comportó en una prueba como se quería aquí? Oh, veo el comentario. ¡Gracias por la confirmación!
imz - Ivan Zakharyaschev
- Sí, lo intenté antes de responder. Cuando fileen su ejemplo anterior hay un enlace simbólico, el enlace se deja solo y el archivo real cambia.
mattdm
@mattdm: Sí, ¡gracias por la confirmación! (Noté sus palabras "eso funciona" un poco más tarde de lo que escribí mi comentario).
imz - Ivan Zakharyaschev
3

Si sabe a somefileciencia cierta que es un enlace simbólico, puede proporcionar explícitamente la ruta completa al archivo con lo siguiente:

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

De esa manera, el enlace simbólico original permanece intacto ya que el reemplazo ahora se realiza directamente con el archivo original.

IDDQD
fuente
Pero la salida de readlinktodavía puede ser otro enlace simbólico. Lo mejor sería usar -fo -ecuando esté disponible o zsh's $file:Po (:P)calificador global como se muestra en @ikegami para obtener una ruta sin enlace simbólico.
Stéphane Chazelas
3

Si se trata de un solo archivo que, el comando se vería de la siguiente manera:

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

La solución entonces es la siguiente:

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

Esto maneja enlaces simbólicos y no simbólicos.


Si se trata de una cantidad arbitrariamente grande de archivos, el comando se vería de la siguiente manera:

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

La solución entonces es la siguiente:

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

Esto maneja enlaces simbólicos y no simbólicos.


La advertencia readlink no es un comando estándar, por lo que su presencia, sus argumentos y su funcionalidad varían según el sistema, así que asegúrese de verificar su documentación. Por ejemplo, algunas implementaciones tienen -f(que también podría usar aquí), pero no -e, algunas tampoco. busyboxSe readlink -fcomporta como GNU readlink -e. Y algunos sistemas tienen un realpathcomando en lugar de readlinkque generalmente se comporta como GNU readlink -f.

Tenga en cuenta que con GNU readlink -e, los enlaces simbólicos que no se pueden resolver a un archivo existente se eliminan silenciosamente.

ikegami
fuente
@ Stéphane Chazelas, $var:Pno funciona para mí. ( X='tmp' zsh -c 'perl -E"say for @ARGV" $X:P'salidas tmp:P)
ikegami el
necesita zsh 5.3 (2016) o superior para $var:P. Con versiones anteriores, siempre puede usar $var:Aen su lugar. Consulte el manual para conocer las diferencias y zsh.org/mla/users/2016/msg00593.html para conocer los motivos de la introducción :P(la realpath()interfaz real ).
Stéphane Chazelas
( :Ase agregó en 4.3.10 (2009), GNU readlink -zen 2012, no conozco ninguna otra readlinkimplementación que admita -z). Tenga en cuenta que con GNU xargs, generalmente desea la -ropción a menos que pueda garantizar que la entrada no estará vacía. Aquí, no es gran cosa (a menos que el perlcódigo contenga una declaración BEGIN/ END), solo recibiría una -i used with no filenames on the command line, reading from STDIN.advertencia si eso sucede.
Stéphane Chazelas
Vaya, leí mal -rla documentación. Pensé que hice lo contrario de lo que hace. Readded
ikegami el