Eu tenho pastas aninhadas com um monte de arquivos dentro que são vinculados uns aos outros. Eu gostaria de quebrar os hardlinks (convertê-los em arquivos separados), mas imediatamente converter cada par em um reflink (para que eles tenham inodes diferentes, mas usem a mesma seção do disco).
find -type f -links +1
encontrará todos os hardlinks, enquanto um comando como
cp --reflink=always my_file.bin my_file_copy.bin
irá copiar um arquivo sem usar mais espaço em disco, criando-o como um reflink.
Como faço para combiná-los para percorrer todo um conjunto de pastas aninhadas e converter cada hardlink em um reflink, substituindo-os pelo mesmo nome de arquivo?
Você marcou
ubuntu
, eu entendo que você não está limitado a ferramentas estritamente POSIX e suas opções POSIX.Notas:
my_file.bin
hardlink se tornamy_file.bin
reflink. Não haverámy_file_copy.bin
. (Esta nota é no caso de você querer criarmy_file_copy.bin
reflink deixandomy_file.bin
o hardlink intacto. A questão não é clara neste assunto, ela introduzmy_file_copy.bin
por algum motivo.)mktemp
oucp
falhar, entãomv
não será executado. Em qualquer caso, você não deve perder o conteúdo original, a menos que algum outro processo modifique o arquivo temporário.find
testa os arquivos um por um, ele nunca substituirá (converterá) todos os links físicos para qualquer inode. Se todos os hardlinks forem processados atéfind
então-links +1
falhará no último. O inode original sobreviverá. Isso significa que se o arquivo original estiver aberto e for modificado no local (sem alterar o número do inode), a modificação sobreviverá em algum lugar (mas é difícil dizer antecipadamente qual hardlink será processado por último e manterá seu número inode). Uma situação em que um arquivo aberto é totalmente desvinculado, modificado como tal e removido do sistema de arquivos assim que é fechado não deve acontecer.cp
oumv
falhar, o arquivo temporário sobreviverá. Você pode querer capturar stderr em um arquivo (2>some_file
) e investigar mais tarde.-print
agirá se o código do shell for bem-sucedido. Só está lá para que você possa ver algo acontecer.find-sh
é explicado aqui: Qual é o segundo sh emsh -c 'some shell code' sh
?Edit: Como apontado por Kamil, não faça o
for x in $(find ...)
. Usar ofind -execdir sh -c
formato é a maneira correta de usar a saída de localização. Vou deixar minha resposta aqui no entanto.Você pode escrever um pequeno script Bash ou escrever diretamente um loop for em seu shell bash:
$ for filename in $(find -type f -links +1); do echo "I found this file: ${filename}"; done
Este exemplo pegará cada linha do
find
comando e a colocará em uma${filename}
variável que você poderá usar. Aqui, estamos apenas imprimindo umI found this file: $filename
para cada um, mas você pode substituir isso pelo seu comando de cópia, que provavelmente seria algo assim:$ for filename in $(find -type f -links +1); do echo "Copying ${filename} to ${filename}_copy.bin"; cp --reflink=always ${filename} ${filename}_copy.bin; done
Ou, se você quiser colocar isso em um script Bash para facilitar a leitura e o trabalho. Crie um arquivo
copy_script.sh
com este conteúdo:Em seguida, salve e execute com
$ bash ./copy_script.sh