
Me gustaría rsync
revisar algunos archivos, especificados con una matriz, y eliminar cualquier otro archivo en un directorio.
El único enfoque que se me ocurre es eliminar otros archivos usando find
y rsync
sobre los archivos, de modo que copiemos la menor cantidad de archivos posible.
En el siguiente ejemplo, quiero eliminar cualquier otro archivo en /tmp/tmp/
, excepto btrfs_x64.efi
y 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 {} +
Quiero que la expansión se expanda al siguiente comando:
$ find /tmp/tmp/drivers_x64/ ! -name btrfs_x64.efi ! -name iso9660_x64.efi -type f -exec rm -f {} +
Pero en lugar de eso parece estar ejecutando el siguiente comando:
$ find /tmp/tmp/drivers_x64/ "! -name btrfs_x64.efi" "! -name iso9660_x64.efi" -type f -exec rm -f {} +
¿Hay alguna manera de conseguir lo primero? Idealmente, también funciona si algunas entradas de la matriz tienen espacios.
Respuesta1
Sí, aquí necesitas generar 3 argumentos find
para cada elemento de tu matriz. También find
toma un patrón, por lo que -name
para que coincida con los nombres de archivo exactos, deberá omitir los find
operadores comodín ( *
, ?
y ):[
\
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 a tantos elementos como haya en la matriz, después de realizar la sustitución en cada uno de ellos.
Aquí, dado que -name
lleva un archivo.nombrepatrón, no debe contener /
, por lo que puede reemplazar cada elemento !/-name/element
y luego dividirlo en /
:
set -o extendedglob # for (#m)
find ${refind_efi_dir}drivers_x64/ \
${(@s[/])${drivers//(#m)[?*[\\]/\\$MATCH}/#/!\/-name\/} \
-type f -exec rm -f {} +
O utilícelo $'\0'
en lugar de, /
ya que de todos modos no se puede pasar como argumento a un comando externo:
set -o extendedglob # for (#m)
find ${refind_efi_dir}drivers_x64/ \
${(@0)${drivers//(#m)[?*[\\]/\\$MATCH}/#/!$'\0'-name$'\0'} \
-type f -exec rm -f {} +
Pero eso no ayuda mucho con la legibilidad...
Aquí también puedes usar zsh
's glob para todo:
(cd -P -- $refind_efi_dir && rm -f -- **/^(${(~j[|])drivers})(D.))
Donde el j[|]
indicador de expansión de parámetros une los elementos de la $drivers
matriz |
y ~
hace que |
se trate como un operador global. Ese patrón se niega con ^
(para lo cual necesita la extendedglob
opción). D
para incluir archivos ocultos, .
para restringir a archivos normales como su -type f
.
Respuesta2
"${drivers[@]/#/! -name }"
coloca ! -name
la misma palabra de shell que cada elemento de la matriz. "${=drivers[@]/#/! -name }"
dividiría los resultados, por lo que !
y -name
terminaría como palabras de shell separadas, pero los elementos de la matriz también se dividirían en espacios en blanco.
Una solución es abusar del e
yP
clasificatorios globales:
find … /(e\''reply=($drivers)'\'P\''!'\'P\''-name'\') …
/(…)
expande el patrón global /
, que siempre coincide exactamente con una cosa (el directorio raíz), y le aplica los calificadores globales. El calificador global e
reemplaza cada coincidencia por el nuevo valor de la variable de matriz reply
, que configuramos en la lista de palabras a las que queremos aplicar los calificadores globales posteriores. Luego, el calificador global P
, usado dos veces, inserta el texto especificado como palabras separadas antes de cada coincidencia.
En este caso específico,como demostró Stéphane Chazelas, simplemente puedes hacer todo con zsh globs, sin usar find.