Remover arquivo, mas apenas se for um link simbólico

11

Idealmente, eu gostaria de um comando como este

rm --only-if-symlink link-to-file

porque eu me queimei muitas vezes excluindo acidentalmente o arquivo em vez do link simbólico apontando para o arquivo. Isso pode ser especialmente ruim quando o sudo está envolvido. Agora, é claro, faço um ls -alpara garantir que seja realmente um link simbólico e tal, mas isso é vulnerável a erros do operador (arquivo com nome semelhante, erro de digitação etc.) e condições de corrida (se alguém quiser que eu exclua um arquivo por algum motivo). Existe alguma maneira de verificar se um arquivo é um link simbólico e excluí-lo apenas se estiver em um comando?

Shelvacu
fonte
1
Não acho que a condição de corrida deva ser uma preocupação. Qualquer pessoa que pudesse remover o link simbólico e substituí-lo por um arquivo precisaria de permissão de gravação no diretório, o que significa que também poderia excluir o arquivo.
Node Eldredge

Respostas:

19
 $ rm_if_link(){ [ ! -L "$1" ] || rm -v "$1"; }

 #test
 $ touch nonlink; ln -s link
 $ rm_if_link nonlink
 $ rm_if_link link
   removed 'link'     
PSkocik
fonte
4
Certamente seria mais claro para remover !e mudar ||para&&
glenn jackman
5
@glennjackman Eu criei o hábito de preferir o ||formulário. &&derruba as coisas se você estiver no set -emodo.
PSKocik
@PSkocik Isso é arriscado em geral, pois não faz distinção entre o caso interessante (o arquivo existe e é um link simbólico) e outros motivos para um código de saída diferente de zero, como um valor não citado vazio ( [ ! -L $1 ]), usando um operador inválido ( [ ! -l foo ]) ou mesmo erro de sintaxe ( [ ! -q foo || echo foo)
l0b0 21/03
2
@XiongChiamiov O problema rm_if_link(){ [ -L "$1" ] && rm -v "$1"; }é que executá-lo em um set -e modo não vinculado matará seu processo de shell. Você pode acrescentar, || :mas, então, torna-se, IMHO, ainda menos claro que a ! ||versão, e impedirá que uma rmfalha o mate set -e, o que provavelmente é indesejável. ! ||é bastante conciso e claro, e não sofre de nenhum desses problemas.
PSkocik
1
Não . Outras opções: a) coloque rmdentro de seu teste, b) use uma declaração if real, c) não saia usando -eshells interativos internos (e teste seus scripts!). Se você quiser discutir sobre legibilidade, usar ifpara testar se algo é um link é claramente o vencedor.
Boicote SE para Monica Cellio 23/03
5

Você pode usar finde sua -type lcondição de teste (que testa para verificar se o objeto encontrado é uma tinta l ou não)

Por exemplo, se você tiver um arquivo chamado foono diretório atual, poderá fazer o seguinte:

$ find . -type l -iname "foo" -delete

Você pode simplificar isso com apenas:

$ find . -maxdepth 1 -type l -delete

O que excluiria todos os links simbólicos no diretório atual.

Atenção:

A -deleteopção findé realmente muito perigosa. Certifique-se de colocá-lo no final do comando FIND. Se você o colocar incorretamente, ele excluirá tudo o que encontrar, independentemente de os resultados corresponderem ao seu condicional.

Conforme sugerido nos comentários, uma opção mais segura pode ser usar finde rm -i(o que força você a confirmar a remoção do arquivo) em conjunto:

$ $ find . -type l -iname "foo" | xargs rm -i

Pessoalmente, uso -exec trash {} \;para excluir arquivos temporariamente, porque, como você, já foram queimados rmno passado. Double vai para uma -deletebandeira mal colocada .

http://slackermedia.ml/trashy

Klaatu von Schlacker
fonte
1
Em vez de -delete, você pode simplesmente imprimi-lo e passá-lo para o xargs: find . -type l -iname "foo" | xargs rm -i é mais seguro.
precisa
1
Eu gosto do uso do @BobanP. rm -iPor segurança, com certeza.
Klaatu von Schlacker
3
"Isso excluiria todos os links simbólicos no diretório atual" ... e qualquer ponto abaixo dele.
Joseph R.
1
Como @JosephR. dito, ele percorrerá todo o caminho. Adicione -maxdepth 1 na localização.
precisa
1
Ainda quebra nos nomes de arquivos com espaços. Sugira usar -print0para finde -0para xargs.
Curinga
4
zsh -c 'rm foo(@)'

@é um qualificador glob ; o padrão foo(@)corresponde ao que foocorresponde, mas apenas aos links simbólicos.

foo(-@)corresponderia apenas a links simbólicos quebrados. foo(@,L0)corresponderia apenas a links simbólicos e arquivos vazios.

Obviamente, se você estiver executando o zsh, basta digitar rm foo(@). Você precisa tomar cuidado para não pressionar Enterantes de digitar (@).

Gilles 'SO- parar de ser mau'
fonte
1
O Zsh soa cada vez mais poderoso quando vejo respostas como esta.
Jeff Schaller