rsync usando regex para incluir solo algunos archivos

11

Estoy tratando de ejecutar rsync para copiar algunos archivos de forma recursiva en una ruta basada en su patrón de nombre de archivo, sin distinción entre mayúsculas y minúsculas . Esto es lo que he hecho para ejecutar rsync:

$ rsync -avvz --include ='*/' --include='.*[Nn][Aa][Mm][E].*' --exclude='*' ./a/ ./b/

No se copia nada, la salida de depuración muestra:

[sender] hiding file 1Name.txt because of pattern *
[sender] hiding file 1.txt because of pattern *
[sender] hiding file 2.txt because of pattern *
[sender] hiding file Name1.txt because of pattern *
[sender] hiding directory test1 because of pattern *
[sender] hiding file NaMe.txt because of pattern *

He intentado usar: --include='*[Nn][Aa][Mm][E]*'y otras combinaciones pero todavía no funciona.

¿Alguna idea sobre cómo usar regex para incluir algunos archivos?

usuario1957413
fuente
44
¿Por qué estás usando el --exclude='*'?
2
por lo que excluye todo lo que no es parte de la inclusión.
'ocultando el archivo 1Name.txt debido al patrón ' esto indica: - "¿eso - excluye la regla debe estar en el comando?" o Si desea excluir algunos archivos, entonces por qué un " ".
Akshay Patil

Respuestas:

5

rsync no habla regex. Puedes alistar find y grep, aunque se vuelve un poco arcano. Para encontrar los archivos de destino:

find a/ |
grep -i 'name'

Pero todos tienen el prefijo "a /", lo cual tiene sentido, pero lo que queremos terminar es una lista de patrones de inclusión aceptables para rsync, y como el prefijo "a /" no funciona para rsync I ' Lo quitaré con corte:

find . |
grep -i 'name' |
cut -d / -f 2-

Todavía hay un problema: seguiremos perdiendo archivos en subdirectorios, porque rsync no busca directorios en la lista de exclusión. Voy a usar awk para agregar los subdirectorios de cualquier archivo coincidente a la lista de patrones de inclusión:

find a/ |
grep -i 'name' |
cut -d / -f 2- |
awk -F/ '{print; while(/\//) {sub("/[^/]*$", ""); print}}'

Todo lo que queda es enviar la lista a rsync; podemos usar el argumento --include-from = - para proporcionar una lista de patrones para rsync en la entrada estándar. Entonces, en conjunto:

find a/ |
grep -i 'name' |
cut -d / -f 2- |
awk -F/ '{print; while(/\//) {sub("/[^/]*$", ""); print}}' |
rsync -avvz --include-from=- --exclude='*' ./a/ ./b/

Tenga en cuenta que se hace referencia al directorio de origen 'a' a través de dos rutas diferentes: "a /" y "./a/". Esto es sutil pero importante. Para hacer las cosas más consistentes, haré un cambio final y siempre me referiré al directorio de origen como "./a/". Sin embargo, esto significa que el comando de corte tiene que cambiar ya que habrá un "./" adicional en la parte frontal de los resultados de find:

find ./a/ |
grep -i 'name' |
cut -d / -f 3- |
awk -F/ '{print; while(/\//) {sub("/[^/]*$", ""); print}}' |
rsync -avvz --include-from=- --exclude='*' ./a/ ./b/
sqweek
fuente
Intenté ejecutarlo, encontré problemas con el comando de corte. Parece que -tes un cambio válido.
editar: quise decir -t no es un cambio válido
lo siento, debería ser -d. comencé a usar sed y luego cambié a cortar porque pensé que era más claro, pero olvidé editar mis comandos: S
Seguimiento: Intenté editar el script para tomar argumentos ($ 1 = path_to_search, $ 2 como patrón para egrep) ya que estoy haciendo coincidir el nombre de archivo + la combinación de extensiones. Que las partes funcionan bien, obtuve la lista esperada, sin embargo, rsync no puede copiar. Parece que solo funciona con el directorio de caracteres de un solo nombre, como en el ejemplo (a). Supongo que el comando de corte debe modificarse para cortar caracteres según el directorio principal o de origen. Un poco perdido de cómo hacer eso:
user1957413
Ah sí, tienes toda la razón. Debería funcionar en un nombre de directorio de cualquier longitud, pero fallará tan pronto como se refiera a un directorio fuera del directorio actual (porque habrá una cantidad diferente de barras en la parte del prefijo). Para solucionarlo, probablemente sea más fácil usar sed en lugar de cortar, como: sed "s#^$1/*##" buuuut que se romperá en las rutas que contienen un #. Para solucionarlo, tenemos que citar el nombre del directorio entrante: prefix=$(echo "$1" | sed 's#/#\\/#g')y luego sed "s/^$prefix\\/*//" Las posibilidades de las citas bash son un poco pesadillas;)
sqweek
7

Sugeriría usar la opción de filtro de rsync. Para su ejemplo simplemente escriba:

rsync -vam -f'+ *[Nn][Aa][Mm][E]*' -f'+ */' -f'- *' a b

la primera regla de filtro le dice a rsync qué patrones incluir. La segunda regla es necesaria para indicarle a rsync que inspeccione todos los directorios en su recorrido. Para evitar la inclusión de directorios vacíos, se excluyen explícitamente por -mopción. La última regla de filtro le dice a rsync que elimine todos los patrones restantes que todavía no coinciden hasta ahora.

chispa
fuente
Dulce. Esto funcionó también. Estaba obteniendo la carpeta a dentro de b, que se solucionó usando a / b / como origen y destino. ¡Gracias!
user1957413
Use -f '+ * [Nn] [Aa] [Mm] [E] **' (dos estrellas al final) para incluir el contenido de todos los directorios con un nombre específico.
fóbico
2

Si usa ZSH, puede usar la bandera (#i) para desactivar la distinción entre mayúsculas y minúsculas. Ejemplo:

$ touch NAME
$ ls (#i)*name*
NAME

ZSH también admite exclusiones, que se especifican al igual que la ruta normal pero tienen un ~ inicial

$ touch aa ab ac
$ ls *~*c
aa ab

Puede encadenar exclusiones:

$ ls *~*c~*b
aa

Finalmente, puede especificar qué tipo de archivo desea que se devuelva (directorio, archivo, etc.). Esto se hace con (/) para el directorio y (.) Para el archivo.

$ touch file
$ mkdir dir
$ ls *(.)
file

Basado en todo esto, haría ese comando como:

rsync -avvz *(/) (#i)*name* ./a/ ./b/

(No veo la necesidad de una exclusión con estos selectores)

Matthew Franglen
fuente
1

La respuesta de @ sqweek anterior es increíble, aunque sospecho que tiene un error en su awkscript para generar directorios principales, ya que me da, por ejemplo:

$ echo a/b/c/d | awk -F/ '{print; while(/\//) {sub("/[^/]*", ""); print}}'
a/b/c/d
a/c/d
a/d
a

Pude solucionarlo usando en su gensublugar:

$ echo a/b/c/d | awk -F/ '{print; while(/\//) { $0=gensub("(.*)/[^/]*", "\\1", "g"); print}}'
a/b/c/d
a/b/c
a/b
a

Entonces, su solución completa, con el awkbit cambiado, sería:

find ./a/ |
grep -i 'name' |
cut -d / -f 3- |
awk -F/ '{print; while(/\//) { $0=gensub("(.*)/[^/]*", "\\1", "g"); print}}' |
rsync -avvz --include-from=- --exclude='*' ./a/ ./b/
Ryan Williams
fuente
Gracias. Edité mi respuesta con la solución equivalente de anclar la expresión regular al final de la línea ( sub("/[^/]*$")).
semana de
0

Intenté con un script de C # ya que es el lenguaje con el que tengo más experiencia. Puedo crear la lista de archivos que quiero incluir, pero alguien rsync todavía me dice que haga una caminata. Crea las carpetas, pero ignora los archivos. Esto es lo que obtuve.

Primero el contenido del directorio:

~/mono$ ls -l
total 24
drwxr-xr-x 5 me me 4096 Jan 15 00:36 a
drwxr-xr-x 2 me me 4096 Jan 15 00:36 b
drwxr-xr-x 3 me me 4096 Jan 14 00:31 bin
-rw-r--r-- 1 me me 3566 Jan 15 00:31 test.cs
-rwxr-xr-x 1 me me 4096 Jan 15 00:31 test.exe
-rwxr--r-- 1 me me  114 Jan 14 22:40 test.sh

Luego, la salida del script C #:

~/mono$ mono test.exe

/a/myfile/myfileseries.pdf
/a/myfile2/testfile.pdf

Y la salida de depuración:

~/mono$ mono test.exe | rsync -avvvz --include='*/' --include-from=- --exclude='*' ./a/ ./b/
[client] add_rule(+ */)
[client] parse_filter_file(-,20,3)
[client] add_rule(+ /a/myfile/myfileseries.pdf)
[client] add_rule(+ /a/myfile2/testfile.pdf)
[client] add_rule(- *)
sending incremental file list
[sender] make_file(.,*,0)
[sender] hiding file 1Name.txt because of pattern *
[sender] showing directory myfile2 because of pattern */
[sender] make_file(myfile2,*,2)
[sender] hiding file 1.txt because of pattern *
[sender] hiding file 2.txt because of pattern *
[sender] hiding file Name1.txt because of pattern *
[sender] showing directory test1 because of pattern */
[sender] make_file(test1,*,2)
[sender] hiding file NaMe.txt because of pattern *
[sender] showing directory myfile because of pattern */
[sender] make_file(myfile,*,2)
send_file_list done
send_files starting
[sender] hiding file myfile/myfileseries.pdf because of pattern *
[sender] hiding file myfile2/testfile.pdf because of pattern *
[sender] hiding file test1/test.txt because of pattern *
usuario1957413
fuente
0

[EDITAR] Esto solo funciona localmente. Para rutas remotas, primero se debe crear la estructura de directorios.

Más simple que la respuesta aceptada; Use --file-from, que incluye directorios principales automáticamente e imprime la ruta del archivo con% P

find /tmp/source -wholename '*[Nn][Aa][Mm][E]*' -printf '%P\n' | rsync -vzrm --exclude='*/' --files-from=- /tmp/source/ /tmp/target/

Entonces solo tienes que usar findy rsync.

fóbico
fuente