Converta o link simbólico absoluto em link simbólico relativo com o comando simples do Linux

29

Eu tenho um sub-sistema de arquivos completo dentro de um caminho /home/user/systemque contém a estrutura padrão do Linux com diretórios /bin, /home, /root, /usr, /var, /etc, ...

Este sub-sistema de arquivos contém links simbólicos, relativos ou absolutos. Os links simbólicos relativos são ótimos, eles ficam no sub-sistema de arquivos abaixo /home/user/system. Mas links simbólicos absolutos são problemáticos, pois apontam para um alvo fora do sistema de subarquivos.

Como exemplo, assumimos um link simbólico absoluto da seguinte maneira (visto dentro do sub-sistema de arquivos):

/usr/file1 -> /usr/lib/file1

No sistema de arquivos geral, temos um link /home/user/system/usr/file1que agora aponta para um arquivo /usr/lib/file1fora do sub-sistema de arquivos, em vez de um arquivo /home/user/system/usr/lib/file1 dentro do sub-sistema de arquivos.

Eu gostaria de ter um script simples, de preferência uma única linha de comando (rsync, chroot, find, ...) que converta cada link simbólico absoluto para um relativo.

No exemplo dado, esse link relativo se tornaria

/usr/file1 -> ../usr/lib/file1
Alex
fonte
4
Para os interessados ​​em tentar resolver esse Q, eis um usuário com 250k repetições da tentativa da SO: stackoverflow.com/questions/2564634/… . Existem várias soluções em potencial nesse segmento, mas cada uma tem situações específicas com as quais lida e outras que não.
slm

Respostas:

20

Com o symlinksutilitário Mark Lord (oferecido por muitas distribuições; se o seu não tiver, crie-o a partir da fonte ):

chroot /home/user/system symlinks -cr .

Como alternativa, em sistemas que possuem um readlinkcomando e um -lnamepredicado para find(aviso: código não testado):

cd /home/user/system &&
find . -lname '/*' -exec ksh -c '
  for link; do
    target=$(readlink "$link")
    link=${link#./}
    root=${link//+([!\/])/..}; root=${root#/}; root=${root%..}
    rm "$link"
    ln -s "$root${target#/}" "$link"
  done
' _ {} +
Gilles 'SO- parar de ser mau'
fonte
Esta seria uma boa solução! No entanto, o que a expressão _ {} +significa no final da descoberta? Além disso, recebo um erro find: paths must precede expression: kshque parece não fazer sentido (como o caminho precede a expressão ksh).
13283 Alex
@Alex _é $0para o trecho shell, e {} +é substituída pela lista de argumentos que se tornam $1, $2etc., que for link; do …faz um loop sobre (é sinônimo de for link in "$@"; do …). O erro de findé devido a um erro de digitação (de alguma forma, consegui digitar aspas simples em vez de aspas simples ao redor do argumento -lname).
Gilles 'SO- stop be evil'
Agora o script é executado, mas não conforme o esperado. Talvez eu não tenha sido suficientemente preciso, atualizei a pergunta.
13283 Alex
@ Alex Qual é o problema? Acho que o princípio é válido, mas posso ter cometido alguns erros de codificação. Você tentou symlinks? Isso resolveria o seu problema - é basicamente para isso que ele foi projetado (com o incômodo de ter que executá-lo com chroot (o fakechroot deve fazer o truque)).
Gilles 'SO- stop be evil'
1
Eu recomendo fortemente uma solução pronta para uso, sem a necessidade de instalar algo adicional.
Alex13:
4

Pure bash & coreutils, altera links simbólicos para parentes sem ../s desnecessários no caminho:

find . -type l | while read l; do
    target="$(realpath "$l")"
    ln -fs "$(realpath --relative-to="$(dirname "$(realpath -s "$l")")" "$target")" "$l"
done

Você pode mudar:

  • find .para find /path/to/directoryconverter links simbólicos nesse diretório
  • ln -fsa echo ln -fspara uma corrida seca

Explicação:

  • target="$(realpath "$l")" - encontra o caminho absoluto para o destino do link simbólico
  • ln -fs- cria ligação simbólica ( -s), forçando ( -f) a reescrever as existentes
  • realpath -s "$l" - encontra o caminho absoluto para o próprio link simbólico
  • dirname "$(realpath -s "$l")" - localiza o caminho absoluto para o diretório que contém o link simbólico
  • realpath --relative-to="$(dirname "$(realpath -s "$l")")" "$target" - localiza o caminho do destino em relação ao link simbólico, em outras palavras: converte absoluto em caminho relativo
LUMIFAZA
fonte
1
Eu sugiro adicionar uma ifcláusula para pular links quebrados.
fuenfundachtzig 15/07
0

Esse snippet do bash deve fazer isso. O primeiro formulário imprimirá o que faria; o segundo fará isso. Eu analisaria cuidadosamente a saída, pois é destrutiva - ln -fsubstitui o link existente.

TOP=/home/user/system
cd $TOP
find * -type l -print | while read l; do echo ln -sf $TOP$(readlink $l) $l; done
find * -type l -print | while read l; do      ln -sf $TOP$(readlink $l) $l; done
qneill
fonte
Além do fato de que isso falhará em nomes com espaços, não é o contrário? Prefixa o caminho absoluto?
fuenfundachtzig 15/07
0

Aqui está uma solução sh pura.

cd / home / usuário / sistema &&
encontrar . -lname '/ *' |
enquanto lê l; Faz
  echo ln -sf $ (echo $ (echo $ l | sed 's | / [^ /] * | / .. | g') $ (link de leitura $ l) | sed 's /.....//' ) $ l
feito |
sh
Diomidis Spinellis
fonte
0

A transformação absoluta em links relativos é suportada pelo sshfs, que monta diretórios remotos via ssh.

O comando é: Não

sudo sshfs <remote_user>@<remote_ip_address>:/ /home/<host_user>/mntpoint/ -o transform_symlinks -o allow_other

O comando, especialmente o <placeholders>, deve ser adaptado ao ambiente específico.

user260694
fonte