Substituir links simbólicos por arquivos

31

Existe uma maneira fácil de substituir todos os links simbólicos pelo arquivo ao qual eles vinculam?

Marty Trenouth
fonte
3
Que problema você está tentando resolver?
Daniel Beck
3
Eu tenho um monte de links simbólicos para outra unidade. Eu preciso mover todos os arquivos dessa unidade que são referenciados em outro lugar para que eu possa removê-lo.
Marty Trenouth

Respostas:

14

Para algumas definições de "fácil":

#!/bin/sh
set -e
for link; do
    test -h "$link" || continue

    dir=$(dirname "$link")
    reltarget=$(readlink "$link")
    case $reltarget in
        /*) abstarget=$reltarget;;
        *)  abstarget=$dir/$reltarget;;
    esac

    rm -fv "$link"
    cp -afv "$abstarget" "$link" || {
        # on failure, restore the symlink
        rm -rfv "$link"
        ln -sfv "$reltarget" "$link"
    }
done

Execute este script com nomes de links como argumentos, por exemplo, através de find . -type l -exec /path/tos/script {} +

gravidade
fonte
2
Obrigado pela edição, anon, mas o script faz nomes punho com espaços muito bem, citando variáveis em todos os lugares necessários. Mudar "$var"para "${var}"é um noop no sh / bash. Eu removi o bashism ( [[), o resto é compatível com o POSIX sh.
# Grawity #
Um método ainda mais robusto para lidar com falhas de links simbólicos é colocar cpem um arquivo temporário no mesmo diretório e depois em mv -fum arquivo temporário no original. Isso deve evitar problemas, como pessoas pressionando Ctrl-Cou o sistema de arquivos sendo preenchido entre os comandos rme cp(impedindo assim mesmo o lnfuncionamento do novo ). Os nomes de arquivos temporários são fáceis de localizar ls -apara limpeza posterior, se necessário.
Mr Fooz
Como uma observação lateral, eu realmente não me lembro por que não usei apenas abstarget=$(readlink -f "$link")o bloco de casos , mas pode ter sido por razões de portabilidade.
grawity
17

Pode ser mais fácil usar o tar para copiar os dados para um novo diretório.

-H      (c and r mode only) Symbolic links named on the command line will
        be followed; the target of the link will be archived, not the
        link itself.

Você poderia usar algo assim

tar -hcf - sourcedir | tar -xf - -C newdir

tar --help:
-H, --format=FORMAT        create archive of the given format
-h, --dereference          follow symlinks; archive and dump the files they point to
Steve
fonte
1
Adoro sua solução, mas não entendo a última parte da sua mensagem. Para mim você deve fazer:tar -hcf - sourcedir | tar -xf - -C newdir
Seb DA ROCHA
1
Concordado, essa resposta precisaria ser atualizada conforme o comentário.
astabada
1
cp -Lé a solução mais simples
plesiv 18/09/2015
@plesiv true e compatível com posix, mas nem todas as cpimplementações copiarão diretórios.
precisa saber é o seguinte
16

Se eu entendi corretamente, a -Lbandeira do cpcomando deve fazer exatamente o que você deseja.

Basta copiar todos os links simbólicos e eles serão substituídos pelos arquivos para os quais eles apontam.

RedX
fonte
4
Graças - quase o que eu precisava: Eu tinha que fazer algo como: cp -L files tmp/ && rm files && cp tmp/files . Se você esclarecer, você provavelmente vai ajudar mais pessoas ...
sage
5

"fácil" será uma função sua, provavelmente.

Provavelmente eu escreveria um script que usa o utilitário de linha de comando "find" para encontrar arquivos simbolicamente vinculados e depois chama rm e cp para remover e substituir o arquivo. Você provavelmente faria bem em também chamar a ação pela verificação de localização, para que haja espaço livre suficiente antes de mover o link sym também.

Outra solução pode ser montar o sistema de arquivos em questão através de algo que oculte os links sym (como o samba) e, em seguida, copie tudo disso. Mas, em muitos casos, algo assim introduziria outros problemas.

Uma resposta mais direta à sua pergunta é provavelmente "sim".

Editar: conforme a solicitação de informações mais específicas. De acordo com a página de manual do find , este comando listará todos os arquivos de links sym em uma profundidade de 2 diretórios, em /:

find / -maxdepth 2 -type l -print

( isso foi encontrado aqui )

Para conseguir encontrar para executar algo em encontrá-lo:

find / -maxdepth 2 -type l -exec ./ReplaceSymLink.sh {} \;

Acredito que isso chamará algum script que acabei de inventar e passará o nome do arquivo que você acabou de encontrar. Como alternativa, você pode capturar a saída find para o arquivo (use "find [blah]> symlinks.data", etc) e depois passe esse arquivo para um script que você escreveu para lidar com a cópia do original normalmente.

James T Snell
fonte
? Um pouco genérico - você pode elaborar?
Linker3000
2
A sintaxe é-exec ./ReplaceSymLink.sh {} \;
grawity
Isso é para encontrar os itens e o marcado como resposta é usado para substituir os links!
Marty Trenouth
3

Este é o oneliner que eu usei: suponha que todos os arquivos locais sejam links para outro arquivo em "source". Você pode refinar a seleção de arquivos com padrões ou "localizar". Por favor, entenda antes de usar.

para f em *; do cp --remove-destination source / $ f $ f; feito

Espero que isto ajude

MastroGeppetto
fonte
que tal usar o readlink: para f em *; do cp ---- remove-destination "$ (readlink $ f)" "$ f"; feito
Diego
Sim, é uma boa ideia, mas você perde algum controle sobre a operação. Um limite da minha solução é que ele não funciona para arquivos com espaços em branco no meio ou para listas muito longas. Provavelmente a solução final é reunir o 'find --exec' acima com o 'remove destination'. Alguém quer tentar?
MastroGeppetto
espaços em branco no meio devem ser atenuados colocando "$ f" entre aspas duplas. Aqui está como eu o uso com "find & xargs" em vez de "for loop": find ./ -type l -print0 | xargs -0 -n1 -i sh -c 'cp --remove-destination $ (readlink "{ } ")" {} "'Publiquei como uma resposta separada para a visibilidade. superuser.com/a/1329543/499386
Diego #
2
find . -type l -exec cp --dereference --recursive '{}' '{}'.dereferenced \;

Ele fará uma cópia de cada arquivo / pasta com link simbólico <filename>.dereferenced, o que é mais seguro (se menos conveniente) do que apenas substituí-los diretamente. Mover os dados copiados para os nomes dos arquivos dos links simbólicos é deixado como um exercício para o leitor.

blahdiblah
fonte
2

Usando algumas das idéias anteriores, o comando a seguir substituirá recursivamente todos os links simbólicos por uma cópia do original:

find . -type l -exec bash -c "echo 'Replacing {} ...';  cp -LR '{}' '{}'.dereferenced;  rm '{}';  mv '{}'.dereferenced '{}'" \;
Antonio
fonte
2
find ./ -type l -print0|xargs -0 -n1 -i sh -c 'cp --remove-destination $(readlink "{}") "{}" '

baseado em https://superuser.com/a/1301199/499386 da MastroGeppetto

Diego
fonte
1

Muitas respostas boas lá, mas eu desde que você estava procurando por algo fácil

for i in *; do link=$(readlink $i) && rm $i && mv $link $i; done
xaocon
fonte
Um pequeno aprimoramento ajudará a encontrar links simbólicos: para o lnk no `find. -tipo l`; do src = $ (readlink $ lnk) && rm $ lnk && mv $ src $ lnk; feito (não testado com a hierarquia, porém, ter cuidado com acentos graves)
Roman Susi
1

Eu tive o mesmo problema com o gwenview ao copiar links quando você normalmente espera que ele siga o link e copie o arquivo.

O que fiz para 'limpar' o diretório, seguindo todos os links e copiando todos os arquivos apontados para o diretório, foi criar um diretório inicial, executar por exemplo

"for file in `ls`; do cp -L $file scratchdir/$file; done; mv scratchdir/* ./" 

isso é muito perigoso para colocar em um script, pois não há verificação, mas foi corrigido o meu problema.

user225929
fonte
1
for f in *; do cp --remove-destination $(readlink "$f") "$f"; done
Lorem Ipsum Dolor
fonte
0

Eu fiz uma versão em uma linha, pois o meu hotel na web não permitia bash-scripts e funcionou bem para mim:

before:
> find . -type l | wc -l
358

> for link in `find . -type l` ; do echo "$link : "; ls -al $link ; cp $link notalink; unlink $link; mv notalink $link ; ls -al $link; done
...

after:
> find . -type l | wc -l
0
Autotrader
fonte