Converti symlink assoluto in symlink relativo con un semplice comando Linux


29

Ho un completo sub-file system all'interno di un percorso /home/user/systemcontenente la struttura standard di Linux con le directory /bin, /home, /root, /usr, /var, /etc, ...

Questo sottosistema contiene collegamenti simbolici, relativi o assoluti. I relativi link simbolici vanno bene, rimangono all'interno del sottosistema sotto /home/user/system. Ma i symlink assoluti sono problematici, in quanto puntano a un target esterno al sottosistema.

Ad esempio, assumiamo un collegamento simbolico assoluto come segue (visto all'interno del sottosistema):

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

Nel filesystem generale abbiamo un collegamento /home/user/system/usr/file1che ora punta a un file /usr/lib/file1esterno al sottosistema, anziché a un file /home/user/system/usr/lib/file1 all'interno del sottosistema.

Vorrei avere un semplice script, preferibilmente una singola riga di comando (rsync, chroot, find, ...) che converte ogni link simbolico assoluto in uno relativo.

Nell'esempio dato, quel collegamento relativo diventerebbe

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

4
Per coloro che sono interessati a tentare di risolvere questo Q, ecco un utente con risposta a 250k dal tentativo di SO: stackoverflow.com/questions/2564634/… . Ci sono diverse potenziali soluzioni in quel thread, ma ognuna ha situazioni specifiche che affronta e altre che non lo fanno.
slm

Risposte:


20

Con l' symlinksutilità di Mark Lord (offerta da molte distribuzioni; se la tua non ce l'ha, costruiscila dalla fonte ):

chroot /home/user/system symlinks -cr .

In alternativa, sui sistemi che hanno un readlinkcomando e un -lnamepredicato a find(attenzione: codice non testato):

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
' _ {} +

Questa sarebbe una bella soluzione! Tuttavia, cosa significa l'espressione _ {} +alla fine della ricerca? Inoltre, ricevo un errore find: paths must precede expression: kshche non sembra avere senso (poiché il percorso precede l'espressione ksh).
Alex,

@Alex _è $0per il frammento di shell, e {} +viene sostituito dall'elenco di argomenti che diventano $1, $2ecc che for link; do …loop sopra (è sinonimo di for link in "$@"; do …). L'errore findè dovuto a un errore di battitura (in qualche modo sono riuscito a digitare backquotes invece di virgolette singole attorno all'argomento -lname).
Gilles 'SO- smetti di essere malvagio' il

Ora lo script viene eseguito, ma non come previsto. Forse non sono stato abbastanza preciso, ho aggiornato la domanda.
Alex,

@Alex Qual è il problema? Penso che il principio sia valido, ma potrei aver commesso degli errori di programmazione. Ci hai provato symlinks? Risolverà il tuo problema - è sostanzialmente quello per cui è stato progettato (con il fastidio che devi eseguirlo chroot ( fakechroot dovrebbe fare il trucco)).
Gilles 'SO- smetti di essere malvagio' il

1
Propongo vivamente una soluzione pronta all'uso, senza la necessità di installare qualcosa di aggiuntivo.
Alex,

4

Bash puro e coreutils, cambia i symlink in relativi senza ../s inutili nel percorso:

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

Si può cambiare:

  • find .per find /path/to/directoryconvertire i collegamenti simbolici in quella directory
  • ln -fsper echo ln -fsun funzionamento a secco

Spiegazione:

  • target="$(realpath "$l")" - trova il percorso assoluto verso la destinazione del link simbolico
  • ln -fs- crea symlink ( -s), forzando ( -f) riscrivere di esistente
  • realpath -s "$l" - trova il percorso assoluto per il collegamento simbolico stesso
  • dirname "$(realpath -s "$l")" - trova il percorso assoluto della directory contenente il collegamento simbolico
  • realpath --relative-to="$(dirname "$(realpath -s "$l")")" "$target" - trova il percorso del target rispetto al link simbolico, in altre parole: converte il percorso assoluto in percorso relativo

1
Suggerirei di aggiungere una ifclausola per saltare i collegamenti interrotti.
fuenfundachtzig,

0

Questo frammento di bash dovrebbe farlo. Il primo modulo stamperà cosa farebbe; il secondo lo farà. Esaminerei attentamente l'output in quanto è distruttivo: ln -fsovrascrive il collegamento esistente.

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

Oltre al fatto che questo fallirà per i nomi con spazi, non è forse il contrario? Prefisso il percorso assoluto?
fuenfundachtzig,

0

Ecco una soluzione sh pura.

cd / home / user / system &&
trova . -lname '/ *' |
mentre leggi l; fare
  echo ln -sf $ (echo $ (echo $ l | sed 's | / [^ /] * | / .. | g') $ (readlink $ l) | sed 's /.....//' ) $ l
fatto |
sh

0

La trasformazione dell'assoluto in collegamenti relativi è supportata da sshfs che monta directory remote tramite ssh.

Il comando è: Ecco

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

Il comando, in particolare il <placeholders>, deve essere adattato all'ambiente specifico.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.