Entendo muito bem a noção de hardlinks e li as páginas de manual para obter ferramentas básicas como cp
--- e até as especificações recentes do POSIX --- várias vezes. Ainda fiquei surpreso ao observar o seguinte comportamento:
$ echo john > john
$ cp -l john paul
$ echo george > george
Neste ponto john
e paul
terá o mesmo nodo (e conteúdo), e george
serão diferentes em ambos os aspectos. Agora fazemos:
$ cp george paul
Nesse ponto, eu esperava george
e paul
ter números de inode diferentes, mas o mesmo conteúdo - essa expectativa foi atendida -, mas também esperava paul
agora ter um número de inode diferente john
e john
ainda ter o conteúdo john
. Foi aqui que fiquei surpresa. Acontece que copiar um arquivo para o caminho de destino paul
também resulta na instalação desse mesmo arquivo (mesmo inode) em todos os outros caminhos de destino que compartilham paul
o inode. Eu estava pensando que cp
cria um novo arquivo e o move para o local anteriormente ocupado pelo arquivo antigo paul
. Em vez disso, o que parece fazer é abrir o arquivo existente paul
, truncá-lo e gravargeorge
o conteúdo desse arquivo existente. Portanto, qualquer "outro" arquivo com o mesmo inode recebe "seu" conteúdo atualizado ao mesmo tempo.
Ok, esse é um comportamento sistemático e, agora que sei esperar, posso descobrir como contornar ou tirar proveito disso, conforme apropriado. O que me intriga é onde eu deveria ver esse comportamento documentado? Eu ficaria surpreso se não estiver documentado em algum lugar nos documentos que eu já olhei. Mas, aparentemente, eu senti falta disso, e agora não consigo encontrar uma fonte que discuta esse comportamento.
cp
documenta que substitui o arquivo de destino se o arquivo de destino já estiver presente. Você está certo que não especifica em detalhes o que "substituir" significa, mas definitivamente diz "substituir", não "substituir". Se você quer ser pedante, pode argumentar que "substituir" é exatamente o quecp
faz, e o comportamento que você esperava seria chamado apropriadamente de "substituir".Observe também que, se
cp
"substituir" os arquivos de destino preexistentes, isso pode ser considerado surpreendente ou incorreto, provavelmente mais do que "sobrescrever". Por exemplo:cp
primeiro excluísse o arquivo antigo e depois criasse um novo, haveria um intervalo de tempo durante o qual o arquivo estaria ausente, o que seria surpreendente.cp
primeiro criou um arquivo temporário e depois o moveu no lugar, provavelmente deveria documentar isso, devido ao fato de que esses arquivos temporários com nomes estranhos seriam ocasionalmente notados ... mas não é.cp
não foi possível criar um novo arquivo no mesmo diretório do arquivo antigo devido a permissões, isso seria lamentável (especialmente se ele já havia excluído o antigo).cp
e o usuário em questãocp
não eraroot
, seria impossível corresponder o proprietário e as permissões do novo arquivo com os do novo arquivo.cp
não conhecem, eles serão perdidos na cópia. Atualmente, as implementações decp
devem entender coisas como atributos estendidos, mas nem sempre foi assim. E há outras coisas, como garfos de recursos do MacOS ou, para sistemas de arquivos remotos, basicamente qualquer coisa.Então, em conclusão: agora você sabe o que
cp
realmente faz. Você nunca ficará surpreso com isso de novo! Honestamente, acho que a mesma coisa pode ter acontecido comigo também, há muitos anos.fonte
man
páginas paracp
versões do BSD (pelo menos OSX) e do Gnucp
não são tão explícitas sobre "substituição". Essa palavra é usada apenas nos comentários sobre as opções-i
e-n
. A manpage Gnu é especialmente informativo, começandoCopy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.
a BSD / manpage Mac, pelo menos dizIn the first synopsis form, the cp utility copies the contents of the source_file to the target_file.
‘cp’ copies files (or, optionally, directories). The copy is completely independent of the original.
Vejo que o padrão POSIX 2013 especifica o comportamento observado . Diz:
fonte
cp
que daria resultados semelhantesmv
e quebre todos os hardlinks dos quais o dest fazia parte. Mas agora que penso nisso, isso significaria que teria que ser especificamenteunlink(2)
o target (cp -f
) ou criar um temporário de nome diferente e depoisrename(2)
. A implementação direta é apenas abrir o arquivo para substituição, que é o que o POSIX exige. É equivalente acat src > dest
Se você pode dizer, “copiar um arquivo para o caminho de destino
paul
também copia o mesmo arquivo (mesmo inode) para todos os outros caminhos de destino que compartilhampaul
o inode.”, Lamento dizer que você não entende a noção de links físicos muito bem. Se eu der uma maçã a Sir McCartney, eu dou uma maçã a Paul e uma maçã ao parceiro de composição de John Lennon. Mas não dei três maçãs; Eu dei uma maçã para uma pessoa que tem vários nomes / títulos / descritores.Da mesma forma, quando você copia
george
parapaul
, também não está copiandojohn
. Em vez disso, você está copiando osgeorge
dados para o arquivo cujo inode é apontado pelapaul
entrada do diretório.Passo a passo: quando você faz
você criou um novo arquivo (assumindo que ainda não havia um arquivo nomeado
john
nesse diretório). Ou, para falar mais estritamente, isso pressupõe que já não havia uma entrada de diretório com o nomejohn
nesse diretório (porque, falando estritamente, não há arquivos nos diretórios; somente entradas de diretório que apontam para inodes). Depois que você fazou
você não criou um novo arquivo; em vez disso, você deu um novo nome ao arquivo existente. Agora você tem um arquivo com dois nomes:
john
epaul
. E quando você dizvocê está substituindo esse arquivo . O fato de ter dois nomes é irrelevante; poderia ter 42 nomes, possivelmente em lugares que você não pode acessar, e esse comando não estaria copiando os
george\n
dados para todos esses nomes (caminhos); está apenas copiando os dados para o arquivo que possui vários nomes.fonte
john
epaul
comecei como dois nomes de caminho para o mesmo arquivo. Mas era a maneira mais fácil de pensar em me expressar. Não acho que a mera noção de vínculo físico, entendida corretamente, dite um dos dois comportamentos paracp
(sem-l
).