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 find
el nombre correcto?
Obviamente quiero:
foo\ bar
o:
"foo bar"
EDITAR : No quiero pasar xargs
, quiero obtener una cadena con el formato correcto find
para poder canalizar la cadena de nombres de archivo directamente a otro programa.
-exec
bandera confind
? potencialmente podría aliviar este error y hacer que su comando sea más eficiente al hacerlo en-exec
lugar de canalizarlo a otros comandos. Just my $ .02find
formatea 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. .find
ofrecer una opción para generar nombres de archivo en un formato adecuado para el shell. En general, sin embargo, la extensión-print0
GNUfind
funciona 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$IFS
para 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$HOME
se 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
find
soportes-print0
yxargs
soportes-0
:-0
la 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 directory
ls: cannot access bar: No such file or directory
$(..)
entre comillas dobles"$(..)"
find
yxargs
.El uso
-print0
es una opción, pero no todos los programas admiten el uso de flujos de datos delimitados por nullbytes, por lo que tendrá que usarxargs
la-0
opción para algunas cosas, como señaló la respuesta de Gnouc.Una alternativa sería usar
find
's-exec
u-execdir
opciones. El primero de los siguientes alimentará los nombres de archivo asomecommand
uno 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 extglob
en 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
./*.txt
en su lugar, para usar cada * .txt en el directorio de trabajo.find
tiene 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
-exec
acción de búsqueda para resolver este tipo de problema. O, si es necesario, loxargs
que permite la ejecución paralela.Sin embargo, hay una manera de llegar
find
a producir una lista de nombres de archivos legibles por bash. Como era de esperar, utiliza-exec
ybash
, en particular, una extensión delprintf
comando: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
ls
escenario 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')"
eval
es 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