txt script reemplazar por para otro mover extension copiar con como buscar archivos archivo linux macos unix command-line xargs

linux - script - ¿Cómo puedo usar xargs para copiar archivos que tienen espacios y comillas en sus nombres?



mover archivos por extension linux (21)

Aquí hay una solución portátil (POSIX), es decir, una que no requiere find , xargs o cp GNU extensiones específicas:

find . -name "*FooBar*" -exec sh -c ''cp -- "$@" ~/foo/bar'' sh {} +

Manejará correctamente los archivos y directorios con espacios incrustados, líneas nuevas o lo que sea, y es más eficiente (es decir, más rápido ) que las respuestas aceptadas y la mayoría, si no todas, las demás.

Estoy intentando copiar un montón de archivos debajo de un directorio y varios de ellos tienen espacios y comillas simples en sus nombres. Cuando trato de encadenar find y grep con xargs , obtengo el siguiente error:

find .|grep "FooBar"|xargs -I{} cp "{}" ~/foo/bar xargs: unterminated quote

¿Alguna sugerencia para un uso más robusto de xargs?

Esto está en Mac OS X 10.5.3 (Leopard) con BSD xargs .


Busque en el uso de la opción de línea de comandos --null para xargs con la opción -print0 en find.


Con Bash (no POSIX) puede usar la sustitución de procesos para obtener la línea actual dentro de una variable. Esto le permite usar comillas para escapar de caracteres especiales:

while read line ; do cp "$line" ~/bar ; done < <(find . | grep foo)


Creé un pequeño script de contenedor portátil llamado "xargsL" alrededor de "xargs" que soluciona la mayoría de los problemas.

Al contrario de xargs, xargsL acepta un nombre de ruta por línea. Las rutas de acceso pueden contener cualquier carácter excepto (obviamente) nueva línea o bytes NUL.

No se permiten ni se admiten citas en la lista de archivos: los nombres de sus archivos pueden contener todo tipo de espacios en blanco, barras invertidas, comillas invertidas, caracteres comodín de shell y similares: xargsL los procesará como caracteres literales, sin daño alguno.

Como una característica adicional, xargsL no ejecutará el comando una vez si no hay entrada.

Note la diferencia:

$ true | xargs echo no data no data $ true | xargsL echo no data # No output

Cualquier argumento dado a xargsL se pasará a través de xargs.

Aquí está el script de shell POSIX "xargsL":

#! /bin/sh # Line-based version of "xargs" (one pathname per line which may contain any # amount of whitespace except for newlines) with the added bonus feature that # it will not execute the command if the input file is empty. # # Version 2018.76.3 # # Copyright (c) 2018 Guenther Brunthaler. All rights reserved. # # This script is free software. # Distribution is permitted under the terms of the GPLv3. set -e trap ''test $? = 0 || echo "$0 failed!" >& 2'' 0 if IFS= read -r first then { printf ''%s/n'' "$first" cat } | sed ''s/.///&/g'' | xargs ${1+"$@"} fi

Coloque el script en algún directorio en su $ PATH y no se olvide de

$ chmod +x xargsL

El script allí para hacerlo ejecutable.


Es posible que necesite grep Foobar directorio como:

find . -name "file.ext"| grep "FooBar" | xargs -i cp -p "{}" .


Este método funciona en Mac OS X v10.7.5 (Lion):

find . | grep FooBar | xargs -I{} cp {} ~/foo/bar

También probé la sintaxis exacta que publicaste. Eso también funcionó bien en 10.7.5.


Esto es más eficiente ya que no ejecuta "cp" varias veces:

find -name ''*FooBar*'' -print0 | xargs -0 cp -t ~/foo/bar


He encontrado que la siguiente sintaxis funciona bien para mí.

find /usr/pcapps/ -mount -type f -size +1000000c | perl -lpe '' s{ }{// }g '' | xargs ls -l | sort +4nr | head -200

En este ejemplo, estoy buscando los 200 archivos más grandes de más de 1,000,000 de bytes en el sistema de archivos montado en "/ usr / pcapps".

El trazador de líneas de Perl entre "buscar" y "xargs" escapa / cita cada espacio en blanco, por lo que "xargs" pasa cualquier nombre de archivo con espacios en blanco incrustados a "ls" como un único argumento.


Jugué un poco con esto, comencé a contemplar la modificación de xargs y me di cuenta de que para el tipo de caso de uso del que estamos hablando aquí, una simple reimplementación en Python es una mejor idea.

Por un lado, tener ~ 80 líneas de código para todo esto significa que es fácil descubrir qué está sucediendo, y si se requiere un comportamiento diferente, puede piratearlo en un nuevo script en menos tiempo del que se necesita para obtener una respuesta en algún lugar como desbordamiento de pila.

Consulte https://github.com/johnallsup/jda-misc-scripts/blob/master/yargs y https://github.com/johnallsup/jda-misc-scripts/blob/master/zargs.py .

Con yargs como está escrito (y Python 3 instalado) puede escribir:

find .|grep "FooBar"|yargs -l 203 cp --after ~/foo/bar

para hacer la copia de 203 archivos a la vez. (Aquí, 203 es solo un marcador de posición, por supuesto, y usar un número extraño como 203 deja claro que este número no tiene otro significado).

Si realmente quieres algo más rápido y sin la necesidad de Python, toma zargs y yargs como prototipos y reescribe en C ++ o C.


La forma más fácil de hacer lo que quiere el póster original es cambiar el delimitador de cualquier espacio en blanco al carácter de fin de línea como este:

find whatever ... | xargs -d "/n" cp -t /var/tmp


Para aquellos que confían en comandos, aparte de find, por ejemplo, ls :

find . | grep "FooBar" | tr //n //0 | xargs -0 -I{} cp "{}" ~/foo/bar


Para mí, estaba tratando de hacer algo un poco diferente. Quería copiar mis archivos .txt en mi carpeta tmp. Los nombres de archivo .txt contienen espacios y caracteres de apóstrofe. Esto funcionó en mi Mac.

$ find . -type f -name ''*.txt'' | sed ''s/''"''"''//'"''"''/g'' | sed ''s/.*/"&"/'' | xargs -I{} cp -v {} ./tmp/


Puedes combinar todo eso en un solo comando de find :

find . -iname "*foobar*" -exec cp -- "{}" ~/foo/bar /;

Esto manejará nombres de archivos y directorios con espacios en ellos. Puede usar -name para obtener resultados que -name entre mayúsculas y minúsculas.

Nota: el indicador -- pasado a cp impide que se procesen archivos que comienzan con - como opciones.


Si está utilizando Bash, puede convertir stdout a una matriz de líneas por mapfile :

find . | grep "FooBar" | (mapfile -t; cp "${MAPFILE[@]}" ~/foobar)

Los beneficios son:

  • Está incorporado, por lo que es más rápido.
  • Ejecute el comando con todos los nombres de archivos de una vez, para que sea más rápido.
  • Puede añadir otros argumentos a los nombres de archivo. Para cp , también puedes:

    find . -name ''*FooBar*'' -exec cp -t ~/foobar -- {} +

    Sin embargo, algunos comandos no tienen dicha característica.

Las desventajas:

  • Tal vez no se escale bien si hay demasiados nombres de archivos. (¿El límite? No lo sé, pero lo había probado con un archivo de lista de 10 MB que incluye más de 10000 nombres de archivos sin problemas, bajo Debian)

Bueno ... ¿quién sabe si Bash está disponible en OS X?


Si las versiones find y xarg de su sistema no admiten los -print0 y -0 (por ejemplo, AIX find y xargs), puede usar este código de aspecto terrible:

find . -name "*foo*" | sed -e "s/''////'/g" -e ''s/"///"/g'' -e ''s/ /// /g'' | xargs cp /your/dest

Aquí sed se encargará de escapar de los espacios y las citas de xargs.

Probado en AIX 5.3


Tenga en cuenta que la mayoría de las opciones discutidas en otras respuestas no son estándar en plataformas que no usan las utilidades GNU (Solaris, AIX, HP-UX, por ejemplo). Consulte la especificación POSIX para el comportamiento ''estándar'' de xargs.

También encuentro que el comportamiento de xargs por el cual ejecuta el comando al menos una vez, incluso sin entrada, es una molestia.

Escribí mi propia versión privada de xargs (xargl) para lidiar con los problemas de los espacios en los nombres (solo las nuevas líneas son separadas, aunque la combinación ''find ... -print0'' y ''xargs -0'' es bastante buena, dado que los nombres de los archivos no pueden contiene caracteres ASCII NUL ''/ 0''. Mi xargl no es tan completo como debería ser para que valga la pena publicar, especialmente porque GNU tiene instalaciones que son al menos tan buenas.


Tuve el mismo problema. Así es como lo resolví:

find . -name ''*FoooBar*'' | sed ''s/.*/"&"/'' | xargs cp ~/foo/bar

Utilicé sed para sustituir cada línea de entrada con la misma línea, pero entre comillas dobles. Desde la página de sed man, " ... Un símbolo" ("& '''') que aparece en el reemplazo se reemplaza por la cadena que coincide con el RE ... " - en este caso .* , Toda la línea.

Esto resuelve el error de xargs: unterminated quote .


La versión Perl de bill_starr no funcionará bien para las nuevas líneas incrustadas (solo se ocupa de los espacios). Para aquellos en, por ejemplo, Solaris, donde no tiene las herramientas GNU, una versión más completa podría ser (usando sed) ...

find -type f | sed ''s/.///&/g'' | xargs grep string_to_find

ajuste los argumentos de búsqueda y grep u otros comandos que necesite, pero el sed arreglará sus líneas / espacios / tabulaciones incrustados.


Usé la respuesta de Bill Star ligeramente modificada en Solaris:

find . -mtime +2 | perl -pe ''s{^}{/"};s{$}{/"}'' > ~/output.file

Esto pondrá comillas alrededor de cada línea. No utilicé la opción ''-l'' aunque probablemente ayudaría.

La lista de archivos a la que iba sin embargo podría tener ''-'', pero no nuevas líneas. No he usado el archivo de salida con ningún otro comando, ya que quiero revisar lo que se encontró antes de comenzar a eliminarlos masivamente a través de xargs.


find . -print0 | grep --null ''FooBar'' | xargs -0 ...

No sé si grep admite --null , ni si xargs compatible con -0 , en Leopard, pero en GNU todo está bien.


find | perl -lne ''print quotemeta'' | xargs ls -d

Creo que esto funcionará de manera confiable para cualquier personaje, excepto para el salto de línea (y sospecho que si tienes feeds de línea en tus nombres de archivo, entonces tienes problemas más graves que esto). No requiere recursos de búsqueda de GNU, solo Perl, por lo que debería funcionar prácticamente en cualquier lugar.