Código anterior:
total=`ls -Rp rootfs | wc -l`
count=0
Cuando asigno una simple suma a una variable:
sudo find rootfs -exec cp -d -- "{}" "/media/$USER/{}" 2> /dev/null \; -exec sync \; -exec count=$((count+1)) \; -exec echo -en "\rcopiati: $count/$total" \;
Yo obtengo:
find: ‘count=1’: No such file or directory
También cuando ejecuto:
sudo find rootfs -exec cp -d -- "{}" "/media/$USER/{}" 2> /dev/null \; -exec sync \; -exec count=1 \; -exec echo -en "\rcopiati: $count/$total" \;
Me sale el mismo error. ¿Por qué?
Para cada archivo copiado quiero el contador: 1/13444, que se actualiza a 2/13444, 3/13444, etc.
editar:
Encontré un método pero no ve archivos ocultos, ¿cómo puedo hacer que los vean en un bucle for?
#!/bin/bash
copysync() {
countfiles() {
for f in $1/*; do
if [ -d "$f" ]; then
countfiles "$f"
else
if [ "${f: -2}" != "/*" ]; then
total=$((total+1))
fi
fi
done
}
recursivecp() {
for f in $1/*; do
if [ -d "$f" ]; then
mkdir -p "/media/$USER/$f"
recursivecp "$f"
else
if [ "${f: -2}" != "/*" ]; then
sudo cp -a "$f" "/media/$USER/$f"
sudo sync
count=$((count+1))
echo -en "\rCopied: $((count*100/total))%"
fi
fi
done
}
total=0
countfiles $1
count=0
recursivecp $1
}
copysync rootfs
Respuesta1
El shell se expande count=$((count+1))
antes de ejecutarse find
.
Luego find
intentará ejecutar el argumento -exec
como un comando. Debe ser un programa o script, no puede ser un shell incorporado o una sintaxis de shell para la asignación de variables.
Contar los archivos encontrados no funciona de esta manera porque find
inicia un nuevo proceso para -exec
, por lo que el resultado de una asignación de variable no estaría disponible en el shell principal.
Sugiero imprimir una línea para cada archivo encontrado y canalizar la salida find
a wc -l
, por ejemplo
find rootfs -exec cp -d -- "{}" "/media/$USER/{}" \; -exec sync \; -print|wc -l
Para obtener algunos resultados al copiar los archivos, puede usar algo como esto:
find rootfs|while IFS= read -r file
do
cp -d -- "$file" "/media/$USER/$file"
sync
count=$((count+1))
echo -en "\rcopiati: $count/$total"
done
Observaciones:
Esto no funciona para nombres de archivos que contienen una nueva línea (y tal vez otros caracteres especiales).
Es posible que el script no funcione si rootfs
contiene subdirectorios. Debe manejar este caso o usar find
las opciones de -maxdepth
y -type f
evitar este problema.
Respuesta2
Parece como si estuvieras intentando ejecutar todos y cada uno de los comandos con -exec
. Esto no funcionaría en el caso general ya que -exec
sólo ejecuta comandos externos.
En su lugar, llame a un único script en línea y déjelo find
actuar como generador de un bucle en ese script:
find rootfs -type f -exec sh -c '
for pathname do
cp -d "$pathname" "/media/$USER" &&
echo . &&
sync
done' sh {} + | wc -l
Esto encontraría todos los archivos normales dentro o debajo del rootfs
directorio. Para lotes de estos archivos, sh -c
se llama a un breve script en línea. Este script copia cada archivo en el directorio indicado, genera un punto seguido de una nueva línea para cada archivo copiado correctamente y llama sync
.
Cuenta wc -l
el número de puntos generados e informa este recuento. No contamos los nombres de ruta en sí, ya que este recuento sería engañoso si algún nombre de ruta contiene un carácter de nueva línea incrustado.
Sin usar find
, esto se puede hacer, por ejemplo bash
, así:
shopt -s globstar dotglob nullglob
for pathname in rootfs/**/*; do
[[ ! -f $pathname ]] && continue
cp -d "$pathname" "/media/$USER" &&
echo . &&
sync
done | wc -l
Esto utiliza un patrón global que contiene el **
global que coincide con los subdirectorios internos si la globstar
opción Shell está configurada. También configuré dotglob
poder ver nombres ocultos y la nullglob
opción de shell para evitar ejecutar el bucle si el patrón no coincide con nada.
Lo mismo, pero con contador:
shopt -s globstar dotglob nullglob
count=0
for pathname in rootfs/**/*; do
[[ ! -f $pathname ]] && continue
cp -d "$pathname" "/media/$USER" &&
count=$(( count + 1 ))
sync
done
printf 'count=%d\n' "$count"