
Gostaria de rsync
examinar alguns arquivos, especificados com uma matriz, e excluir qualquer outro arquivo em um diretório.
A única abordagem que consigo pensar é remover outros arquivos usando find
e rsync
sobre os arquivos, para copiar o mínimo de arquivos possível.
No exemplo a seguir, desejo excluir qualquer outro arquivo /tmp/tmp/
, exceto btrfs_x64.efi
e iso9660_x64.efi
.
$ refind_efi_dir='/tmp/tmp/'
$ drivers=('btrfs_x64.efi' 'iso9660_x64.efi')
$ find ${refind_efi_dir}drivers_x64/ "${drivers[@]/#/! -name }" -type f -exec rm -f {} +
Quero que a expansão se expanda para o seguinte comando:
$ find /tmp/tmp/drivers_x64/ ! -name btrfs_x64.efi ! -name iso9660_x64.efi -type f -exec rm -f {} +
Mas em vez disso parece estar executando o seguinte comando:
$ find /tmp/tmp/drivers_x64/ "! -name btrfs_x64.efi" "! -name iso9660_x64.efi" -type f -exec rm -f {} +
Existe uma maneira de obter o primeiro? Idealmente, também funciona se algumas entradas da matriz contiverem espaços.
Responder1
Sim, aqui você precisa gerar 3 argumentos para find
cada elemento do seu array. Também find
usa -name
um padrão, portanto, para corresponder aos nomes exatos dos arquivos, você precisa escapar dos find
operadores curinga ( *
, ?
, [
e \
):
set -o extendedglob # for (#m)
exclusions=()
for name ($drivers) exclusions+=(! -name ${name//(#m)[?*[\\]/\\$MATCH})
find ${refind_efi_dir}drivers_x64/ $exclusions -type f -exec rm -f {} +
"${array[@]/pattern/replacement}"
se expande para quantos elementos houver no array, após a substituição realizada em cada um deles.
Aqui, dado que -name
leva um arquivonomepadrão, ele não deve conter /
, então você pode substituir cada elemento por !/-name/element
e depois dividir /
:
set -o extendedglob # for (#m)
find ${refind_efi_dir}drivers_x64/ \
${(@s[/])${drivers//(#m)[?*[\\]/\\$MATCH}/#/!\/-name\/} \
-type f -exec rm -f {} +
Ou use $'\0'
em vez de /
, pois não pode ser passado em um argumento para um comando externo de qualquer maneira:
set -o extendedglob # for (#m)
find ${refind_efi_dir}drivers_x64/ \
${(@0)${drivers//(#m)[?*[\\]/\\$MATCH}/#/!$'\0'-name$'\0'} \
-type f -exec rm -f {} +
Mas isso não ajuda muito na legibilidade ...
Aqui, você também pode usar zsh
o glob para tudo:
(cd -P -- $refind_efi_dir && rm -f -- **/^(${(~j[|])drivers})(D.))
Onde o j[|]
sinalizador de expansão do parâmetro une os elementos do $drivers
array |
e ~
faz com que ele |
seja tratado como um operador glob. Esse padrão é negado com ^
(para o qual você precisa da extendedglob
opção). D
para incluir arquivos ocultos, .
para restringir arquivos regulares como o seu -type f
.
Responder2
"${drivers[@]/#/! -name }"
coloca ! -name
a mesma palavra shell que cada elemento da matriz. "${=drivers[@]/#/! -name }"
dividiria os resultados !
e -name
terminaria como palavras de shell separadas, mas os elementos da matriz também seriam divididos em espaços em branco.
Uma solução é abusar do e
eP
eliminatórias globais:
find … /(e\''reply=($drivers)'\'P\''!'\'P\''-name'\') …
/(…)
expande o padrão glob /
, que sempre corresponde exatamente a uma coisa (o diretório raiz) e aplica os qualificadores glob a ele. O qualificador glob e
substitui cada correspondência pelo novo valor da variável de matriz reply
, que definimos para a lista de palavras às quais queremos aplicar os qualificadores glob subsequentes. Em seguida, o qualificador glob P
, usado duas vezes, insere o texto especificado como palavras separadas antes de cada correspondência.
Neste caso específico,como Stéphane Chazelas mostrou, você pode simplesmente fazer tudo com zsh globs, sem usar find.