converter soft-to hardlinks com cp

converter soft-to hardlinks com cp

A página cpdo comando infooferece na opção --preserve=o seguinte:

links
Preserve nos arquivos de destino quaisquer links entre os arquivos de origem correspondentes. Observe quecom -L' or-H', esta opçãopodeconverter links simbólicos em links físicos.

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

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


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

Responder1

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órioc/
  • : > a;: apenas uma maneira rápida de criar um arquivo vazio. É equivalente a echo "" > a. :é um bash embutido que não faz nada, veja help :.
  • ln -s a b: crie um softlink para achamado b. Neste ponto, este é o conteúdo 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
    

    Observe que bé um link simbólico (soft link) e 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 apara bo diretório c. É aqui que a conversão está acontecendo, as opções passadas cpsã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):

    Ao copiar de um link simbólico, `cp' normalmente segue o link apenas quando não está copiando recursivamente.

    Como -aativa a cópia recursiva ( -R), -Hé necessário seguir links simbólicos. -Hsignifica 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. Este é o conteúdo c/após a última etapa (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, como exatamente funciona, pelo que pude descobrir brincando com ele, cp --preserve=linkscombinado -Lou -Hconverterá links simbólicos em links físicos setanto o link quanto o destino estão sendo copiados para omesmodiretório.


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

Responder2

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

Os documentos são um pouco concisos aqui. A questão principal é que -a implica -d e isso implica --no-dereference que é necessário para que seus comandos funcionem conforme o esperado. IE --no-dereference é necessário para parar cp implicitamente seguindo links simbólicos na fonte.

Para verificar e dividir os detalhes demonstrados aqui:

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

Aqui vemos que -d substitui -H conforme vem depois. Portanto, não iremos desreferenciar links simbólicos em primeiro lugar.

$ 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

Aqui vemos que -H agora é respeitado por último e, portanto, os links simbólicos são seguidos na origem, resultando em hardlinks no destino.

$ 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

Vou tornar os documentos um pouco mais explícitos com o seguinte:

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

Observe as entradas: @file{b}é um link simbólico para file normal @file{a}, mas os arquivos no diretório de destino, @file{c/}, são vinculados fisicamente.

  • Desde que @option{-a}implica @option{--preserve=links}, e desde que @option{-H}diz @command{cp}para desreferenciar os argumentos da linha de comando, ele vê dois arquivos com o mesmo número de inode e preserva o link físico percebido.
  • Uma vez que @option{-a}implica @option{--no-dereference}que copiaria o link simbólico, mas o posterior @option{-H}informa @command{cp}para desreferenciar os argumentos da linha de comando, onde verá dois arquivos com o mesmo número de inode. Então a @option{--preserve=links} opção também implícita em @option{-a}preservará o link físico percebido.

Responder3

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 existe “fonte” e “destino”; é literalmente um arquivo com vários nomes equivalentes. Você pode usar o GNU find para identificá-los desta forma:

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

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

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

E então faça com que um script descubra como escolher um dos valores com o mesmo número para que todos os outros sejam vinculados a ele. Talvez o primeiro se torne o alvo, e qualquer outro com o mesmo inode esteja 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, pois links de diretórios diferentes podem ser criados com um destino inválido se o caminho em find (/tmp neste exemplo) não for absoluto. Mas a ideia geral deve funcionar bem.

informação relacionada