Existe uma maneira de fazer com que perl -i não desista de links simbólicos?

12

Um amigo meu ressalta que, se você fizer:

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

quando "somefile" é na verdade um link simbólico, o perl faz exatamente o que os documentos dizem que fará:

Isso é feito renomeando o arquivo de entrada, abrindo o arquivo de saída pelo nome original e selecionando esse arquivo de saída como o padrão para instruções print (). A extensão, se fornecida, é usada para modificar o nome do arquivo antigo e fazer uma cópia de backup.

O que resulta em um novo link simbólico "somefile.bak" apontando para o arquivo real inalterado e um novo arquivo regular alterado "somefile" com as alterações.

Em muitos casos, seguir o link simbólico seria o comportamento desejado (mesmo que ele deixe o local correto do arquivo .bak ambíguo). Existe uma maneira simples de fazer isso, além de testar links simbólicos em um wrapper e manipular o caso adequadamente?

( sedfaz a mesma coisa, pelo que vale a pena.)

mattdm
fonte
1
Chamar vim ou emacs (acho que ambos seguem links simbólicos)? Sério, receio que a resposta seja reimplementar -p -iem seu script.
Gilles 'SO- stop be evil'
O Sed, de fato, não faz a mesma coisa, pelo menos desde a versão 4.2.1, lançada em junho de 2009. Você deve incluir a opção --follow-symlinks para que a edição edite o arquivo vinculado ao invés de prejudicar o link simbólico ; Suponho que isso foi feito para evitar a quebra de scripts existentes, que podem depender do comportamento antigo.
Garrett

Respostas:

6

Gostaria de saber se o pequeno spongeutilitário de uso geral ("absorver entrada padrão e gravar em um arquivo") do moreutils será útil nesse caso e se seguirá o link simbólico.

O autor descreve sponge assim:

Ele soluciona o problema de editar arquivos no local com as ferramentas Unix, a saber, se você apenas redirecionar a saída para o arquivo que está tentando editar, o redirecionamento entrará em vigor (derrotando o conteúdo do arquivo) antes do primeiro comando no pipeline começa a ler o arquivo. Switches como sed -ie perl -i resolvem isso, mas nem todos os comandos que você deseja usar em um pipeline têm essa opção, e você não pode usar essa abordagem com pipelines de vários comandos de qualquer maneira.

Eu normalmente uso esponja um pouco assim:

sed '...' file | grep '...' | sponge file
imz - Ivan Zakharyaschev
fonte
Ei legal, isso funciona. Suspeito que o comentário de Gilles acima seja a resposta "real", mas como ele não fez isso como resposta, e desde que aprendi um novo utilitário, aceitarei este. :)
mattdm
Mas você já testou spongeesse uso na prática? Quanto a mim: ainda não. Você poderia deixar um comentário informando se ele se comportou em um teste da maneira desejada aqui? Oh, eu vejo o comentário. Obrigado pela confirmação!
IMZ - Ivan Zakharyaschev
- sim, eu tentei antes de responder. Quando, fileno exemplo acima, houver um link simbólico, o link será deixado sozinho e o arquivo real será alterado.
mattdm
@mattdm: Sim, obrigado pela confirmação! (Notei suas palavras "que funciona" um pouco mais tarde do que eu escrevi o meu comentário.)
IMZ - Ivan Zakharyaschev
3

Se você sabe que somefileé um link simbólico, é possível fornecer explicitamente o caminho completo do arquivo com o seguinte:

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

Dessa forma, o link simbólico original permanece intacto, pois a substituição agora é feita diretamente com o arquivo original.

IDDQD
fonte
Mas a saída de readlinkainda pode ser outro link simbólico. O melhor seria usar -fou, -equando disponível zsh, $file:Pou (:P)qualificador glob ou, como mostrado pelo @ikegami, para obter um caminho sem links .
Stéphane Chazelas 4/11
3

Se você estiver lidando com um único arquivo, o comando terá a seguinte aparência:

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

A solução é a seguinte:

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

Isso lida com links simbólicos e não simbólicos.


Se você estiver lidando com um número arbitrariamente grande de arquivos, o comando terá a seguinte aparência:

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

A solução é a seguinte:

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

Isso lida com links simbólicos e não simbólicos.


A advertência readlink não é um comando padrão; portanto, sua presença, seus argumentos e sua funcionalidade variam de acordo com o sistema; portanto, verifique sua documentação. Por exemplo, algumas implementações possuem -f(que você também pode usar aqui), mas não -e, outras também. busyboxé readlink -fse comporta como GNU readlink -e. E alguns sistemas têm um realpathcomando em vez do readlinkqual geralmente se comporta como o GNU readlink -f.

Cuidado com o GNU readlink -e, links simbólicos que não podem ser resolvidos para um arquivo existente são removidos silenciosamente.

ikegami
fonte
@ Stéphane Chazelas, $var:Pnão funciona para mim. ( X='tmp' zsh -c 'perl -E"say for @ARGV" $X:P'saídas tmp:P)
ikegami
você precisa do zsh 5.3 (2016) ou superior para $var:P. Nas versões mais antigas, você sempre pode usar $var:A. Consulte o manual para as diferenças e zsh.org/mla/users/2016/msg00593.html para justificativa da introdução :P(a realpath()interface real ).
Stéphane Chazelas
( :Afoi adicionado em 4.3.10 (2009), GNU readlink -zem 2012, não conheço nenhuma outra readlinkimplementação que suporte -z). Observe que, com o GNU xargs, você geralmente deseja a -ropção, a menos que possa garantir que a entrada não fique vazia. Aqui, não é grande coisa (a menos que o perlcódigo contenha uma declaração BEGIN/ END), você receberá um -i used with no filenames on the command line, reading from STDIN.aviso se isso acontecer.
Stéphane Chazelas 4/11
Opa, eu li mal -ra documentação. Eu pensei que fiz o oposto do que faz. Readded.
Ikegami #