Quiero canalizar nombres de archivos a otros programas, pero todos se ahogan cuando los nombres contienen espacios.
Digamos que tengo un archivo llamado.
foo bar
¿Cómo puedo obtener findel nombre correcto?
Obviamente quiero:
foo\ bar
o:
"foo bar"
EDITAR : No quiero pasar xargs, quiero obtener una cadena con el formato correcto findpara poder canalizar la cadena de nombres de archivo directamente a otro programa.

-execbandera confind? potencialmente podría aliviar este error y hacer que su comando sea más eficiente al hacerlo en-execlugar de canalizarlo a otros comandos. Just my $ .02findformatea bien los nombres de archivo; están escritos un nombre por línea. (Por supuesto, esto es ambiguo si un nombre de archivo contiene un carácter de nueva línea). Entonces, el problema es que el extremo receptor se "ahoga" cuando obtiene un espacio, lo que significa que debe decirnos cuál es el extremo receptor si desea una respuesta significativa. .findofrecer una opción para generar nombres de archivo en un formato adecuado para el shell. En general, sin embargo, la extensión-print0GNUfindfunciona bien para muchos otros escenarios (también), y debe aprender a usarla en cualquier caso.ls $(command...)no alimenta la listastdin. Pone la salida de$(command...)directamente en la línea de comando. En ese caso, es el shell el que está leyendo la c, y usará el valor actual de$IFSpara decidir cómo dividir las palabras en la salida. En general, es mejor usarloxargs. No notará un golpe de rendimiento.find -printf '"%p"\n'agregará comillas dobles alrededor de cada nombre encontrado, pero no citará correctamente las comillas dobles en un nombre de archivo. Si los nombres de sus archivos no tienen comillas dobles incrustadas, puede ignorar el problema: o pasarlosed 's/"/&&/g;s/^""/"/;s/""$/"/'. Si sus nombres de archivo terminan siendo manejados por el shell, probablemente debería usar comillas simples en lugar de comillas dobles (de lo contrario,sweet$HOMEse convertirá en algo asísheet/home/you). Y esto todavía no es muy robusto contra los nombres de archivo con nuevas líneas en ellos. ¿Cómo quieres manejar eso?Respuestas:
POSIXLY:
Con
findsoportes-print0yxargssoportes-0:-0la opción le dice a xargs que use el carácter ASCII NUL en lugar de espacio para finalizar (separar) los nombres de archivo.Ejemplo:
fuente
ls $(find . -maxdepth 1 -type f -print0 | xargs -0)tengols: cannot access ./foo: No such file or directoryls: cannot access bar: No such file or directory$(..)entre comillas dobles"$(..)"findyxargs.El uso
-print0es una opción, pero no todos los programas admiten el uso de flujos de datos delimitados por nullbytes, por lo que tendrá que usarxargsla-0opción para algunas cosas, como señaló la respuesta de Gnouc.Una alternativa sería usar
find's-execu-execdiropciones. El primero de los siguientes alimentará los nombres de archivo asomecommanduno a la vez, mientras que el segundo se expandirá a una lista de archivos:Es posible que en muchos casos sea mejor usar el globbing. Si tiene un shell moderno (bash 4+, zsh, ksh), puede obtener un engrosamiento recursivo con
globstar(**). En bash, debes configurar esto:Tengo una línea que dice
shopt -s globstar extgloben mi .bashrc, por lo que esto siempre está habilitado para mí (y también lo son los globos extendidos, que también son útiles).Si no desea la recursividad, obviamente solo use
./*.txten su lugar, para usar cada * .txt en el directorio de trabajo.findtiene algunas capacidades de búsqueda detallada muy útiles, y es obligatorio para decenas de miles de archivos (en ese punto, se encontrará con el número máximo de argumentos del shell), pero para el uso diario a menudo es innecesario.fuente
Personalmente, usaría la
-execacción de búsqueda para resolver este tipo de problema. O, si es necesario, loxargsque permite la ejecución paralela.Sin embargo, hay una manera de llegar
finda producir una lista de nombres de archivos legibles por bash. Como era de esperar, utiliza-execybash, en particular, una extensión delprintfcomando:Sin embargo, si bien eso imprimirá correctamente las palabras con escape de shell, no será utilizable
$(...), ya$(...)que no interpreta las comillas o los escapes. (El resultado$(...)está sujeto a la división de palabras y la expansión del nombre de ruta, a menos que esté entre comillas). Por lo tanto, lo siguiente no hará lo que desea:Lo que deberías hacer es:
(Tenga en cuenta que no he hecho ningún intento real de probar la monstruosidad anterior).
Pero entonces también podrías hacer:
fuente
lsescenario capture adecuadamente el caso de uso del OP, pero esto es solo especulación, ya que no se nos ha mostrado lo que realmente está tratando de lograr. Esta solución en realidad funciona muy bien; Obtengo el resultado que esperaba (vagamente) para todos los nombres de archivos divertidos que intenté, incluidotouch "$(tr a-z '\001-\026' <<<'the quick brown fox jumped over the lazy dogs')"evales que todavía no tiene que pasarlaeval; puede guardarlo en un parámetro y usarlo más tarde, tal vez varias veces con diferentes comandos. Sin embargo, la OP no da ninguna indicación de que ese es el caso de uso (y si lo fuera, tal vez sería mejor poner los nombres de archivo en una matriz, aunque eso es difícil también.)obtendrá los archivos y directorios que contiene espacios
te conseguirá los archivos contiene espacios
obtendrá los directorios contiene espacios
fuente
fuente