converte soft-to hardlinks com cp

5

A página cp do comando info oferece na opção --preserve= o seguinte:

links
Preserve in the destination files any links between corresponding source files. Note that with -L' or-H', this option can convert symbolic links to hard links.

seguido por um exemplo que eu não entendo [agora]; de qualquer forma:

Pergunta : Como transformar softlinks em hardlinks com cp ? E há um caminho de volta também [convertendo-se em softlinks]?


Problema Secundário : Onde o pode na citação acima entra em jogo? Eu entendo o propósito de -L e -H , sou capaz de copiar softlinks totalmente funcionais etc., mas até agora não consegui transformar softlinks em hardlinks.

    
por erch 09.10.2013 / 01:09

3 respostas

5

O exemplo na página de informações mostra como o exemplo é um pouco difícil de seguir:

$ mkdir c; : > a; ln -s a b; cp -aH a b c; ls -i1 c
74161745 a
74161745 b

Vamos dividir isso em seus comandos componentes:

  • mkdir c; : cria o diretório c/
  • : > a; : apenas uma maneira rápida de criar um arquivo vazio. É equivalente a echo "" > a . : é um bash construído em que não faz nada, veja help : .
  • ln -s a b : crie um link para a chamado b . Neste ponto, estes são os conteúdos do diretório atual:

    $ ls -l | cc2ter 
    total 4
    -rw-r--r-- 1 terdon terdon    0 Oct  9 02:50 a
    lrwxrwxrwx 1 terdon terdon    1 Oct  9 02:50 b -> a
    drwxr-xr-x 2 terdon terdon 4096 Oct  9 02:50 c
    

    Note que b é um link simbólico (link suave) não aponta para o mesmo inode que a :

    $ ls -i1c a b
    16647344 a
    16647362 b
    
  • cp -aH a b c; : copie os arquivos a e b no diretório c . Aqui é onde a conversão está acontecendo, as opções passadas para cp são:

    -a, --archive
          same as -dR --preserve=all
    -d    same as --no-dereference --preserve=links
    -H    follow command-line symbolic links in SOURCE
    

    O -H é necessário porque (de info cp ):

    When copying from a symbolic link, 'cp' normally follows the link only when not copying recursively.

    Como -a ativa a cópia recursiva ( -R ), -H é necessário para seguir os links simbólicos. -H significa que os links são seguidos apesar da recursão e resultarão na criação de links físicos no diretório de destino. Estes são os conteúdos de c/ após o último passo (a primeira coluna é o número do inode):

    $ ls -li c 
    total 0
    17044704 -rw-r--r-- 2 terdon terdon 0 Oct  9 02:50 a
    17044704 -rw-r--r-- 2 terdon terdon 0 Oct  9 02:50 b
    

Agora, exatamente como isso funciona, até onde eu consigo descobrir, cp --preserve=links combinado com -L ou -H converterá links simbólicos em links físicos se o link e o alvo está sendo copiado para o mesmo diretório .

Na verdade, como o OP descobriu , pelo menos nos sistemas Debian, cp --preserve=links is suficiente para converter links simbólicos em links físicos se o diretório de destino for o mesmo.

    
por 09.10.2013 / 03:07
1

Enviei um relatório sobre um possível bug para a equipe coreutils @ gnu.org na documentação de info cp e recebi esta resposta:

The docs are a bit terse here. The main issue is that -a implies -d and that implies --no-dereference which is required to get your commands to work as expected. I.E. --no-dereference is required to stop cp implicitly following symlinks in the source.

To verify and split out the detail being demonstrated here:

$ mkdir links; : > a; ln -s a b;

Here we see that -d overrides -H as it comes after. Therefore we will not dereference symlinks in the first place.

$ rm links/*; cp -H -d a b links
$ l links/
lrwxrwxrwx. 1 padraig 1 Oct 10 09:37 b ▪▶ a
-rw-rw-r--. 1 padraig 0 Oct 10 09:37 a

Here we see that -H is now honored as it comes last, and therefore symlinks are followed in the source, resulting in hardlinks in the destination.

$ rm links/*
$ rm links/*; cp -d -H a b links
$ l links
-rw-rw-r--. 2 padraig 0 Oct 10 09:37 b
-rw-rw-r--. 2 padraig 0 Oct 10 09:37 a

I'll make the docs a bit more explicit with the following:

diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index b273627..aeed4ca 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -8257,9 +8257,11 @@ $ mkdir c; : > a; ln -s a b; cp -aH a b c; ls -i1 c
 @noindent

Note the inputs: @file{b} is a symlink to regular file @file{a}, yet the files in destination directory, @file{c/}, are hard-linked.

  • Since @option{-a} implies @option{--preserve=links}, and since @option{-H} tells @command{cp} to dereference command line arguments, it sees two files with the same inode number, and preserves the perceived hard link.
  • Since @option{-a} implies @option{--no-dereference} it would copy the symlink, but the later @option{-H} tells @command{cp} to dereference the command line arguments where it then sees two files with the same inode number. Then the @option{--preserve=links} option also implied by @option{-a} will preserve the perceived hard link.
    
por 10.10.2013 / 16:40
1

Seria difícil converter links físicos em links simbólicos. No caso de um link físico, existe um bloco de dados no sistema de arquivos que possui duas ou mais entradas de arquivo apontando para ele. Não há "fonte" e "destino"; é literalmente um arquivo com vários nomes equivalentes. Você pode usar o GNU find para identificá-los desta maneira:

sauer@zipper:~$ find . -type f -links +1 -printf "%i: %p (%n)\n"
609: ./link1 (2)
609: ./link2 (2)

Uma vez que você tenha todos os arquivos com o mesmo inode, você terá que escolher um para ser o arquivo "real" e depois substituir todos os outros por links simbólicos para o arquivo mestre. Provavelmente, a maneira de fazer isso seria usar isso:

sauer@zipper:~$ find . -type f -links +1 -printf "%i %p\n" | sort -nk1
609 ./link1
609 ./link2

E, em seguida, peça a um script para descobrir como escolher um dos valores com o mesmo número para que todos os outros vinculem-se a ele. Talvez o primeiro se torne o alvo, e qualquer outro com o mesmo inode seja vinculado a ele. Aqui está um exemplo de script de shell realmente simples e não testado

#!/bin/sh
prev=""
target=""
find /tmp -type f -links +1 -printf "%i %p\n" | sort -nk1 \
| while read inode file
do
  if [[ $inode != $prev ]]
  then
     target="$file"
     prev=$inode
  else
    ln -sf "$target" "$file"
  fi
done

Existem problemas potenciais, em que os links de diretórios diferentes podem ser criados com um destino inválido se o caminho na localização (/ tmp neste exemplo) não for absoluto. Mas a ideia geral deve estar bem.

    
por 10.10.2013 / 17:29