O diretório copiado parece ficar maior no destino

O diretório copiado parece ficar maior no destino

Eu tenho o seguinte código como parte de um script de shell:

while [ $(ps -ef | awk '{print $2}' | grep -F "$CPPID") ]; do
    sleep 10
    awk -v "usbsize=$(/bin/df | grep -F $DEVICEMOUNTPOINTQ | awk '{print $3}')" -v "isosize=$(/bin/df | grep -F $ISOMOUNTPOINTQ | awk '{print $3}')" 'BEGIN { printf "%.1f", 100 * usbsize / isosize }' && echo "% copied..."
done

Isso está monitorando cpfazendo a seguinte operação:

cp -a "$ISOMOUNTPOINT"/* "$DEVICEMOUNTPOINT"

E isso funciona bem na maior parte, até

90.5% copied...
94.2% copied...
97.8% copied...
101.6% copied...
102.7% copied...

Por que isso excede 100% do tamanho da fonte? A cópia é de um ISO montado em loop para uma partição formatada em NTFS em uma unidade flash USB. Eu estou supondo que isso é provavelmente uma coisa do sistema de arquivos?

O que falta no meu exemplo para combinar os tamanhos, para que quando cpconcluído seja 100% copiado e não 103%?

Obrigado.


Re: Recompensa

Concederei a recompensa à primeira pessoa que produzir uma solução semelhante ao código acima que atenda aos seguintes critérios:

  • O script deve ser capaz de detectar cópias na proporção de 1:1
  • O script não deve exibir um valor superior a 100% copiado,no entanto...
  • O script não deve simplesmente limitar a exibição a 100% copiada quando exceder esse limite.

Se o tamanho dos dadosfazde fato diferem de origem para destino por algum motivo, então eu gostaria de um script que percebesse isso e ainda exibisse a proporção real copiada.

Responder1

Aqui está seu código simplificado e mais legível:

while ps -p $CPPID > /dev/null
do
    sleep 10
    usbsize=$(/bin/df $DEVICEMOUNTPOINTQ | awk 'NR == 2 {print $3}')
    isosize=$(/bin/df $ISOMOUNTPOINTQ | awk 'NR == 2 {print $3}')
    awk -v "usbsize=$usbsize" -v "isosize=$isosize" 'BEGIN { printf "%.1f%% copied...\n", 100 * usbsize / isosize }'
done

Sua última awklinha poderia ser substituída por estas duas:

    percent=$(echo "$usbsize / $isosize * 100" | bc -l)
    printf "%.1f%% copied...\n" $percent

Então você poderia fazer isso antes dessa printfdeclaração:

if (( $(echo "$percent > 100" | bc) == 1 ))
then
    break
fi

e adicione wait $CPPIDlogo após o final do whileloop. Isso interromperá a impressão do progresso quando 100% for alcançado.

VerGerenciamento de processosem relação à confiabilidade dos PIDs (eles são reciclados).

O problema que você está vendo provavelmente se deve ao uso do valor "usado" do sistema de arquivos de destino em vez do valordiferençano valor atual a partir do valor inicial.

Tente adicionar uma linha como esta antes do whileloop:

startsize=$(/bin/df $DEVICEMOUNTPOINTQ | awk 'NR == 2 {print $3}')

e mude a linha dentro do loop para:

usbsize=$(/bin/df $DEVICEMOUNTPOINTQ | awk -v "start=$startsize" 'NR == 2 {print $3 - start}')

É claro que tudo isso pode ser evitável se você usar rsync --progressem vez de cp.

Editar:

Além disso, tente fazer isso no whileloop conforme mostrado acima para ver quais são os números usados ​​no cálculo. Isso pode fornecer uma pista sobre o que está acontecendo:

    awk -v "usbsize=$usbsize" -v "isosize=$isosize" 'BEGIN { printf "%d of %d, %.1f%% copied...\n", usbsize, isosize, 100 * usbsize / isosize }'

Responder2

A primeira coisa é que isso dependeria muito do tipo de arquivo no diretório de origem. Eu pensaria que o provável culpado são arquivos esparsos. Um arquivo esparso é aquele onde stat.st_size != (stat.st_blksize * stat.st_blocks); isto é, o tamanho geral do arquivo é maior que o número de blocos de dados associados ao inode do arquivo. Quaisquer blocos não alocados são lidos como um bloco de zeros pelas chamadas do sistema. Portanto, quando você usa cp(1) em um arquivo esparso, o arquivo de destino conterá mais blocos (contendo apenas zeros) do que o arquivo de origem. Os comandos du(1) e df(1) analisam o número de blocos, não o tamanho do(s) arquivo(s). Os arquivos principais geralmente são criados como arquivos esparsos, pois podem precisar mapear a memória. Este tipo de arquivo é útil para criar imagens de disco, por exemplo, criar uma unidade de host virtual com tamanho de 15 GB. Seria um grande desperdício alocar todos os blocos no momento da criação; o tamanho (st_size) pode ser 15 GB, mas o número real de blocos pode começar em 0.

Este é apenas um tipo de arquivo que pode explodir quando copiado. Sem saber o que você tem em seu sistema de arquivos, é difícil dizer o que mais pode estar causando isso.

Responder3

Você pode usar o rsync no modo somente local, onde a origem e o destino não possuem um ':' no nome, para que ele se comporte como um comando de cópia aprimorado. Com o parâmetro progress, exibe algo semelhante a isto (fonte) :

$ rsync -r -v --progress -e ssh root@remote-server:~/pictures /home/user/
receiving file list ...
366 files to consider
pictures/IMG_1142.jpg
 4400662 100%   32.21kB/s    0:02:13 (xfer#31, to-check=334/366)
pictures/IMG_1172.jpg
 2457600  71%   32.49kB/s    0:00:29

Como isso não fornece a porcentagem total, outra solução pode ser usar este script (fonte) :

#!/bin/sh
cp_p()
{
strace -q -ewrite cp -- "${1}" "${2}" 2>&1 \
  | awk '{
    count += $NF
        if (count % 10 == 0) {
           percent = count / total_size * 100
           printf "%3d%% [", percent
           for (i=0;i<=percent;i++)
              printf "="
           printf ">"
           for (i=percent;i<100;i++)
              printf " "
           printf "]\r"
        }
     }
     END { print "" }' total_size=$(stat -c '%s' "${1}") count=0
}

Em ação:

% cp_p /mnt/raid/pub/iso/debian/debian-2.2r4potato-i386-netinst.iso /dev/null
76% [===========================================>                    ]

Você também pode dar uma olhadamover arquivos com barra de progressoque detalha como adicionar a cp e mv a opção -g para mostrar o progresso.

informação relacionada