tamaños - ver peso de archivo linux
Itere sobre una lista de archivos con espacios (8)
Como no está haciendo ningún otro tipo de filtrado con find
, puede usar lo siguiente a partir de bash
4.0:
shopt -s globstar
getlist() {
for f in **/foo*
do
echo "File found: $f"
# do something useful
done
}
**/
coincidirá con cero o más directorios, por lo que el patrón completo coincidirá con foo*
en el directorio actual o en cualquier subdirectorio.
Quiero iterar sobre una lista de archivos. Esta lista es el resultado de un comando de find
, así que se me ocurrió:
getlist() {
for f in $(find . -iname "foo*")
do
echo "File found: $f"
# do something useful
done
}
Está bien, excepto si un archivo tiene espacios en su nombre:
$ ls
foo_bar_baz.txt
foo bar baz.txt
$ getlist
File found: foo_bar_baz.txt
File found: foo
File found: bar
File found: baz.txt
¿Qué puedo hacer para evitar la división de espacios?
En algunos casos, aquí si solo necesita copiar o mover una lista de archivos, también puede convertir esa lista en awk.
Importante: /"" "/"
alrededor del campo $0
(en resumen, sus archivos, una lista de líneas = un archivo).
find . -iname "foo*" | awk ''{print "mv /""$0"/" ./MyDir2" | "sh" }''
Hay varias maneras factibles de lograr esto.
Si quisiera mantener su versión original, podría hacerlo de la siguiente manera:
getlist() {
IFS=$''/n''
for file in $(find . -iname ''foo*'') ; do
printf ''File found: %s/n'' "$file"
done
}
Esto aún fallará si los nombres de archivo tienen nuevas líneas literales en ellos, pero los espacios no lo romperán.
Sin embargo, jugar con IFS no es necesario. Esta es mi forma preferida de hacer esto:
getlist() {
while IFS= read -d $''/0'' -r file ; do
printf ''File found: %s/n'' "$file"
done < <(find . -iname ''foo*'' -print0)
}
Si encuentra la sintaxis < <(command)
desconocida, debe leer sobre la sustitución del proceso . La ventaja de esto for file in $(find ...)
es que los archivos con espacios, líneas nuevas y otros caracteres se manejan correctamente. Esto funciona porque find
con -print0
usará un null
(aka /0
) como terminador para cada nombre de archivo y, a diferencia de la línea nueva, null no es un carácter legal en el nombre de un archivo.
La ventaja de esto sobre la versión casi equivalente
getlist() {
find . -iname ''foo*'' -print0 | while read -d $''/0'' -r file ; do
printf ''File found: %s/n'' "$file"
done
}
Es que se conserva cualquier asignación de variable en el cuerpo del ciclo while. Es decir, si canalizas hacia arriba while
estás arriba, entonces el cuerpo del while
está en una subcapa que puede no ser lo que quieres.
La ventaja de la versión de sustitución de proceso sobre find ... -print0 | xargs -0
find ... -print0 | xargs -0
es mínimo: la versión de xargs
está bien si todo lo que necesita es imprimir una línea o realizar una sola operación en el archivo, pero si necesita realizar varios pasos, la versión de bucle es más fácil.
EDITAR : Aquí hay un buen script de prueba para que pueda hacerse una idea de la diferencia entre los diferentes intentos de resolver este problema
#!/usr/bin/env bash
dir=/tmp/getlist.test/
mkdir -p "$dir"
cd "$dir"
touch ''file not starting foo'' foo foobar barfoo ''foo with spaces''/
''foo with''$''/n''newline ''foo with trailing whitespace ''
# while with process substitution, null terminated, empty IFS
getlist0() {
while IFS= read -d $''/0'' -r file ; do
printf ''File found: ''"''%s''"''/n'' "$file"
done < <(find . -iname ''foo*'' -print0)
}
# while with process substitution, null terminated, default IFS
getlist1() {
while read -d $''/0'' -r file ; do
printf ''File found: ''"''%s''"''/n'' "$file"
done < <(find . -iname ''foo*'' -print0)
}
# pipe to while, newline terminated
getlist2() {
find . -iname ''foo*'' | while read -r file ; do
printf ''File found: ''"''%s''"''/n'' "$file"
done
}
# pipe to while, null terminated
getlist3() {
find . -iname ''foo*'' -print0 | while read -d $''/0'' -r file ; do
printf ''File found: ''"''%s''"''/n'' "$file"
done
}
# for loop over subshell results, newline terminated, default IFS
getlist4() {
for file in "$(find . -iname ''foo*'')" ; do
printf ''File found: ''"''%s''"''/n'' "$file"
done
}
# for loop over subshell results, newline terminated, newline IFS
getlist5() {
IFS=$''/n''
for file in $(find . -iname ''foo*'') ; do
printf ''File found: ''"''%s''"''/n'' "$file"
done
}
# see how they run
for n in {0..5} ; do
printf ''/n/ngetlist%d:/n'' $n
eval getlist$n
done
rm -rf "$dir"
Me encantan los bucles y la iteración de matriz, así que creo que agregaré esta respuesta a la mezcla ...
También me gustó el estúpido ejemplo de archivo de marchelbling. :)
$ mkdir test
$ cd test
$ touch "stupid file1"
$ touch "stupid file2"
$ touch "stupid file 3"
Dentro del directorio de prueba:
readarray -t arr <<< "`ls -A1`"
Esto agrega cada línea de listado de archivos en una matriz de bash llamada arr
con cualquier nueva línea final eliminada.
Digamos que queremos dar mejores nombres a estos archivos ...
for i in ${!arr[@]}
do
newname=`echo "${arr[$i]}" | sed ''s/stupid/smarter/; s/ */_/g''`;
mv "${arr[$i]}" "$newname"
done
$ {! arr [@]} se expande a 0 1 2 por lo que "$ {arr [$ i]}" es el elemento i- ésimo de la matriz. Las citas alrededor de las variables son importantes para preservar los espacios.
El resultado es tres archivos renombrados:
$ ls -1
smarter_file1
smarter_file2
smarter_file_3
Podría reemplazar la iteración basada en palabras por una basada en líneas:
find . -iname "foo*" | while read f
do
# ... loop body
done
También hay una solución muy simple: confía en bash globbing
$ mkdir test
$ cd test
$ touch "stupid file1"
$ touch "stupid file2"
$ touch "stupid file 3"
$ ls
stupid file 3 stupid file1 stupid file2
$ for file in *; do echo "file: ''${file}''"; done
file: ''stupid file 3''
file: ''stupid file1''
file: ''stupid file2''
Tenga en cuenta que no estoy seguro de que este comportamiento sea el predeterminado, pero no veo ninguna configuración especial en mi shopt, así que iría y diría que debería ser "seguro" (probado en osx y ubuntu).
find . -iname "foo*" -print0 | xargs -L1 -0 echo "File found:"
find . -name "fo*" -print0 | xargs -0 ls -l
Ver man xargs
.