Script para substituir todos os links simbólicos para um destino para apontar para outro destino (no mesmo diretório)

3

Dado o seguinte exemplo artificial:

$ mkdir -p a/b && touch a/b/original-target && touch a/b/new-target
$ mkdir -p c1/c2 && cd c1/c2 && ln -s ../../a/b/original-target && cd -
$ mkdir -p d1/d2/d3 && cd d1/d2/d3 && ln -s ../../../a/b/original-target && cd -
$ tree .
.
├── a
│   └── b
│       ├── new-target
│       └── original-target
├── c1
│   └── c2
│       └── original-target -> ../../a/b/original-target
└── d1
    └── d2
        └── d3
            └── original-target -> ../../../a/b/original-target

Como substituo todos os links simbólicos para original-target por links simbólicos para new-target . Você pode assumir que tanto o original quanto o novo alvo estão no mesmo diretório (como é o caso no exemplo inventado acima). Os links simbólicos precisam usar caminhos relativos.

    
por Marcus Junius Brutus 07.12.2014 / 21:56

2 respostas

2

Eu suponho que você esteja usando o bash, então eu usaria globstar para percorrer todos os diretórios de uma só vez com ** . Depois disso, tudo o que resta é jogar um pouco com readlink , realpath , etc:

shopt -s globstar
for file in c1/** d1/**; do
    if [[ -h "$file" ]]; then
        if [[ "$(readlink -f "$file")" == "$(realpath a/b/original-target)" ]]; then
            ln -sf "$(dirname "$(readlink "$file")")"/new-target "$(dirname "$file")" && rm -f "$file"
        fi
    fi
done
    
por 07.12.2014 / 23:15
1

pax pode ser realmente útil nesses casos. Na verdade, seria mais fácil se eu descobrisse um pax que faz a opção -o listopt=... especificada pelo POSIX mas, apesar do meu olhar, eu ainda tenho que encontrar um que faça. Eu uso o que o mirabilos mantém - o BSD pax ( mirabipax ?) - que é provavelmente o que a maioria dos outros faz até onde eu sei. De qualquer forma, você começa a regex nomes de arquivos - e listas de arquivos. Por exemplo:

(set -e; mkdir -p a/b c1/c2 d1/d2/d3
touch a/b/original-target a/b/new-target
cd c1/c2; ln -s ../../a/b/original-target
cd ../../d1/d2/d3; ln -s ../../../a/b/original-target)

Isso faz a sua árvore. Agora vou listar:

pax -ws'|\(\..*/original-target.*\)*.*||' ././ | 
pax -cvs'|\(.*\)/original|/new|p' \
     '././a/b/original-target' '*/original-target?*'

Que imprime ...

././d1/d2/d3/original-target >> ././d1/d2/d3/new-target
lrwxrwxrwx  1 mikeserv mikeserv         0 Dec  7 18:17 ././d1/d2/d3/new-target => ../../../a/b/new-target
././c1/c2/original-target >> ././c1/c2/new-target
lrwxrwxrwx  1 mikeserv mikeserv         0 Dec  7 18:14 ././c1/c2/new-target => ../../a/b/new-target

Ok, o primeiro pax escreve um ustar archive para stdout . O -s regex arg I entregue é mais frequentemente usado para alterar nomes de arquivos no fluxo - como eu faço aqui com o segundo pax - mas com um truque ou dois ele também pode ser usado para filtrar arquivos adicionados ao arquivo em o primeiro lugar.

Você vê, qualquer nome de arquivo que termina vazio após a aplicação do -s arg é especificado para ser removido do arquivo completamente. Então, eu uso um regex que corresponda a cada nome de arquivo, mas também remova todos os caracteres de cada um que não correspondam ao padrão salvo em . Isso é necessário porque, embora seja possível selecionar facilmente arquivos dentro de um archive com base em um padrão, esse não é o caso quando -w rite o archive - que depende de globs de shell e, portanto, não recorre.

Ainda assim, o regex como escrito ainda receberá /original-target seguido por qualquer caractere e, do outro lado, um dos padrões que eu -c omplement na minha seleção ao listar o arquivo é */original-target?* - que será solte aí. Mesmo isso não é perfeito - quer dizer, talvez existam links que contenham esse padrão duas vezes, mas ... bem, é o que é - e é muito bom. Pode ser tratado com testes adicionais nas mesmas linhas. De qualquer forma, eu também -c omplico uma correspondência para ././a/b/original-file em si - e isso também é descartado da saída da lista.

No modo de lista - que é o que o pax faz quando não -r ead ou -w rite - pax lista com detalhes - no formato ls -l - incluindo todos os soft vincular destinos como link => target - mas somente depois de aplicar qualquer -s args aplicável. Aqueles mesmos -s args são impressos, independentemente do -v quando o -s|...|...|p o p é aplicado no seguinte formato:

"%s>>%s\n", original pathname, new pathname

Por isso, obtemos as listagens separadas com o ././ que, de outra forma, não apareceria. Agora isso torna as coisas realmente fáceis, como espero que você concorde. Deixarei para o leitor decidir como se pode aplicar esse tipo de dados com ln - deve ser bastante simples - mas você não deveria precisar fazer isso .

Na verdade, pax é especificado para permitir que você altere o arquivo em r ead ou w rite time com opções cli para alterar itens como usuário, grupo, linkdata, nome do arquivo, origem do arquivo Estas são ferramentas de análise de arquivos incrivelmente poderosas - mas o mais perto que cheguei de pax que as implementa é o GNU tar e somente em parte. Eu não encontrei nenhum meio de afetar os tipos de arquivo de um cabeçalho - embora você deva ser capaz de fazer isso até certo ponto - com opções de cli, mesmo que não seja muito difícil fazer isso com um regex no próprio arquivo. Existe um campo de cabeçalho para estas coisas:

... 2 - Represents a symbolic link. The contents of the symbolic link shall be stored in the linkname field.

Outra coisa que realmente quero que um pax faça é -o listopt=... . Aqui está um trecho da seção de exemplos da especificação:

Using the option:

-o listopt="%M %(atime)T %(size)D %(name)s"

...overrides the default output description in Standard Output and instead writes:

-rw-rw--- Jan 12 15:53 2003 1492 /usr/foo/bar

Using the options:

-o listopt='%L\t%(size)D\n%.7' \
-o listopt='(name)s\n%(atime)T\n%T'

...overrides the default output description in Standard Output and instead writes:

/usr/foo/bar -> /tmp   1492
/usr/fo
Jan 12 15:53 1991
Jan 31 15:53 2003

Então, se você encontrar um pax que faz isso ... bem, você sabe quem está procurando.

    
por 08.12.2014 / 05:34