Tenga en cuenta que los archivos también pueden tener nuevas líneas en su nombre de archivo. Por eso hay find -print0y xargs -0.
Daniel Beck
Respuestas:
12
Idealmente, no lo hace de esa manera, porque analizar los nombres de archivo correctamente en un script de shell siempre es difícil (arreglarlo para espacios, aún tendrá problemas con otros caracteres incrustados, en particular, nueva línea). Esto incluso aparece como la primera entrada en la página BashPitfalls.
Dicho esto, hay una manera de hacer casi lo que quieres:
oIFS=$IFS
IFS=$'\n'
find . -name '*.txt' | while read -r i; do
# use "$i" with whatever you're doing
done
IFS=$oIFS
Recuerde también citar $icuando lo use, para evitar otras cosas que interpreten los espacios más adelante. También recuerde $IFSretroceder después de usarlo, porque no hacerlo causará errores desconcertantes más tarde.
Esto tiene otra advertencia adjunta: lo que sucede dentro del whileciclo puede tener lugar en un subshell, dependiendo del shell exacto que esté utilizando, por lo que las configuraciones variables pueden no persistir. La forversión de bucle evita eso, pero al precio que, incluso si aplica la $IFSsolución para evitar problemas con los espacios, se meterá en problemas si finddevuelve demasiados archivos.
En algún momento, la solución correcta para todo esto se hace en un lenguaje como Perl o Python en lugar de shell.
Me gusta la idea de usar Python para evitar todo esto.
Scott C Wilson
12
Úselo find -print0y canalícelo xargs -0, o escriba su propio pequeño programa C y canalícelo a su pequeño programa C. Esto es lo que -print0y -0se inventó para.
Los scripts de shell no son la mejor manera de manejar nombres de archivos con espacios en ellos: puede hacerlo, pero se vuelve torpe.
Puede establecer el "separador de campo interno" ( IFS) en algo más que espacio para la división del argumento de bucle, p. Ej.
ORIGIFS=${IFS}
NL='
'
IFS=${NL}
for i in $(find . -name '*.txt'); do
IFS=${ORIGIFS}
#do stuff
done
IFS=${ORIGIFS}
Restablezco IFSdespués de su uso en find, principalmente porque se ve bien, creo. No he visto ningún problema al configurarlo en nueva línea, pero creo que esto es "más limpio".
Otro método, dependiendo de lo que desee hacer con la salida find, es usar directamente -execcon el findcomando o usarlo -print0y canalizarlo xargs -0. En el primer caso findse encarga del escape del nombre del archivo. En el -print0caso, findimprime su salida con un separador nulo y luego se xargsdivide en esto. Como ningún nombre de archivo puede contener ese carácter (lo que sé), esto también siempre es seguro. Esto es sobre todo útil en casos simples; y generalmente no es un gran sustituto para un forciclo completo .
El uso find -print0combinado con xargs -0es completamente robusto contra los nombres de archivos legales, y es uno de los métodos más extensibles disponibles. Por ejemplo, supongamos que desea una lista de cada archivo PDF dentro del directorio actual. Podrías escribir
Esto encontrará cada PDF (vía -iname '*.pdf') en el directorio actual ( .) y cualquier subdirectorio, y pasará cada uno de ellos como argumento al echocomando. Como especificamos la -n 1opción, xargssolo pasaremos un argumento a la vez a echo. Si hubiéramos omitido esa opción, xargshabría pasado la mayor cantidad posible a echo. (Puede echo short input | xargs --show-limitsver cuántos bytes están permitidos en una línea de comando).
¿Qué hace xargsexactamente?
Podemos ver claramente el efecto que xargstiene en su entrada, y el efecto de -nen particular, mediante el uso de un script que hace eco de sus argumentos de una manera más precisa que echo.
No estoy de acuerdo con los bashbashers, porque bash, junto con el conjunto de herramientas * nix, es bastante experto en el manejo de archivos (incluidos aquellos cuyos nombres tienen espacios en blanco incrustados).
En realidad, findle da un control de grano fino sobre la elección de qué archivos procesar ... En el lado bash, realmente solo necesita darse cuenta de que debe convertir sus cadenas bash words; normalmente mediante el uso de "comillas dobles", o algún otro mecanismo como el uso de IFS, o los hallazgos{}
Tenga en cuenta que en la mayoría de las situaciones no es necesario configurar y restablecer IFS; simplemente use IFS localmente como se muestra en los ejemplos a continuación. Los tres manejan bien los espacios en blanco. Tampoco necesita una estructura de bucle "estándar", porque find's \;es efectivamente un bucle; simplemente ponga su lógica de bucle en una función bash (si no está llamando a una herramienta estándar).
Hay cierta validez en ambas perspectivas. Cuando solo estaba trabajando en mis propios archivos, solo usaba find y no me preocupaba, porque mis archivos no tienen espacios (o retornos de carro) en sus nombres. Pero cuando empiezas a trabajar con archivos de otras personas, tienes que usar técnicas más robustas.
find -print0
yxargs -0
.Respuestas:
Idealmente, no lo hace de esa manera, porque analizar los nombres de archivo correctamente en un script de shell siempre es difícil (arreglarlo para espacios, aún tendrá problemas con otros caracteres incrustados, en particular, nueva línea). Esto incluso aparece como la primera entrada en la página BashPitfalls.
Dicho esto, hay una manera de hacer casi lo que quieres:
Recuerde también citar
$i
cuando lo use, para evitar otras cosas que interpreten los espacios más adelante. También recuerde$IFS
retroceder después de usarlo, porque no hacerlo causará errores desconcertantes más tarde.Esto tiene otra advertencia adjunta: lo que sucede dentro del
while
ciclo puede tener lugar en un subshell, dependiendo del shell exacto que esté utilizando, por lo que las configuraciones variables pueden no persistir. Lafor
versión de bucle evita eso, pero al precio que, incluso si aplica la$IFS
solución para evitar problemas con los espacios, se meterá en problemas sifind
devuelve demasiados archivos.En algún momento, la solución correcta para todo esto se hace en un lenguaje como Perl o Python en lugar de shell.
fuente
Úselo
find -print0
y canalíceloxargs -0
, o escriba su propio pequeño programa C y canalícelo a su pequeño programa C. Esto es lo que-print0
y-0
se inventó para.Los scripts de shell no son la mejor manera de manejar nombres de archivos con espacios en ellos: puede hacerlo, pero se vuelve torpe.
fuente
Puede establecer el "separador de campo interno" (
IFS
) en algo más que espacio para la división del argumento de bucle, p. Ej.Restablezco
IFS
después de su uso en find, principalmente porque se ve bien, creo. No he visto ningún problema al configurarlo en nueva línea, pero creo que esto es "más limpio".Otro método, dependiendo de lo que desee hacer con la salida
find
, es usar directamente-exec
con elfind
comando o usarlo-print0
y canalizarloxargs -0
. En el primer casofind
se encarga del escape del nombre del archivo. En el-print0
caso,find
imprime su salida con un separador nulo y luego sexargs
divide en esto. Como ningún nombre de archivo puede contener ese carácter (lo que sé), esto también siempre es seguro. Esto es sobre todo útil en casos simples; y generalmente no es un gran sustituto para unfor
ciclo completo .fuente
Usando
find -print0
conxargs -0
El uso
find -print0
combinado conxargs -0
es completamente robusto contra los nombres de archivos legales, y es uno de los métodos más extensibles disponibles. Por ejemplo, supongamos que desea una lista de cada archivo PDF dentro del directorio actual. Podrías escribirEsto encontrará cada PDF (vía
-iname '*.pdf'
) en el directorio actual (.
) y cualquier subdirectorio, y pasará cada uno de ellos como argumento alecho
comando. Como especificamos la-n 1
opción,xargs
solo pasaremos un argumento a la vez aecho
. Si hubiéramos omitido esa opción,xargs
habría pasado la mayor cantidad posible aecho
. (Puedeecho short input | xargs --show-limits
ver cuántos bytes están permitidos en una línea de comando).¿Qué hace
xargs
exactamente?Podemos ver claramente el efecto que
xargs
tiene en su entrada, y el efecto de-n
en particular, mediante el uso de un script que hace eco de sus argumentos de una manera más precisa queecho
.Tenga en cuenta que maneja espacios y líneas nuevas perfectamente bien,
lo cual sería especialmente problemático con la siguiente solución común:
Notasfuente
No estoy de acuerdo con los
bash
bashers, porquebash
, junto con el conjunto de herramientas * nix, es bastante experto en el manejo de archivos (incluidos aquellos cuyos nombres tienen espacios en blanco incrustados).En realidad,
find
le da un control de grano fino sobre la elección de qué archivos procesar ... En el lado bash, realmente solo necesita darse cuenta de que debe convertir sus cadenasbash words
; normalmente mediante el uso de "comillas dobles", o algún otro mecanismo como el uso de IFS, o los hallazgos{}
Tenga en cuenta que en la mayoría de las situaciones no es necesario configurar y restablecer IFS; simplemente use IFS localmente como se muestra en los ejemplos a continuación. Los tres manejan bien los espacios en blanco. Tampoco necesita una estructura de bucle "estándar", porque find's
\;
es efectivamente un bucle; simplemente ponga su lógica de bucle en una función bash (si no está llamando a una herramienta estándar).Y dos ejemplos más
'encontrar
also allows you to pass multiple filenames as args to you script ..(if it suits your need: use
+instead
\; `)fuente