Skopiowany katalog wydaje się być większy w miejscu docelowym

2

Mam następujący kod jako część skryptu powłoki:

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

Monitoruje to cpwykonując następującą operację:

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

I to działa w większości dobrze, dopóki

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

Dlaczego przekracza to 100% wielkości źródła? Kopia pochodzi z zamontowanego w pętli ISO na partycji sformatowanej w systemie NTFS na dysku flash USB. Zgaduję, że to prawdopodobnie kwestia systemu plików?

Czego brakuje w moim przykładzie, aby dopasować rozmiary, aby po cpzakończeniu został w 100% skopiowany, a nie 103%?

Dzięki.


Re: Bounty

Przyznam nagrodę pierwszej osobie, która opracuje rozwiązanie podobne do powyższego kodu, które spełnia następujące kryteria:

  • Skrypt musi być w stanie wykryć kopiowanie w stosunku 1: 1
  • Skrypt nie może wyświetlać wartości przekraczającej 100% skopiowanej, jednak ...
  • Skrypt nie może po prostu ograniczać wyświetlania w 100% skopiowanym, gdy go przekroczy.

Jeśli rozmiar danych jest rzeczywiście różnią się od źródła do miejsca przeznaczenia, dla jakiegoś powodu, to chciałbym skrypt, który zauważa to i nadal wyświetla rzeczywisty stosunek kopiowane.

Matthieu Cartier
źródło
niejasne jest to, czego chcesz w końcu: po prostu skopiować pliki za pomocą wiersza polecenia i dać postęp? jak to się ma do tytułu twojego pytania?
akira
Mam działającą konfigurację, która już daje postęp, ale przekracza 100% skopiowanych. Chcę wiedzieć, dlaczego tak jest i jak osiągnąć jeden z celów określonych na końcu.
Matthieu Cartier

Odpowiedzi:

1

Oto twój kod uproszczony i bardziej czytelny:

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

Twój ostatni awkwiersz można zastąpić tymi dwoma:

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

Następnie możesz to zrobić tuż przed tym printfstwierdzeniem:

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

i dodaj wait $CPPIDtuż po zakończeniu whilepętli. To zatrzyma postęp drukowania po osiągnięciu 100%.

Zobacz Zarządzanie procesami dotyczące niezawodności PID (są one poddawane recyklingowi).

Problem, który widzisz, prawdopodobnie wynika z użycia „używanej” wartości docelowego systemu plików, a nie z różnicy między wartością bieżącą a wartością początkową.

Spróbuj dodać taką linię przed whilepętlą:

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

i zmień linię wewnątrz pętli na:

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

Oczywiście można tego wszystkiego uniknąć, jeśli użyjesz go rsync --progresszamiast cp.

Edytować:

Spróbuj również w whilepętli, jak pokazano powyżej, aby zobaczyć, jakie są liczby używane w obliczeniach. To może dać wskazówkę, co się dzieje:

    awk -v "usbsize=$usbsize" -v "isosize=$isosize" 'BEGIN { printf "%d of %d, %.1f%% copied...\n", usbsize, isosize, 100 * usbsize / isosize }'
Dennis Williamson
źródło
Dzięki za pomoc, to jednak musi być przenośne - bc nie jest nawet domyślnie instalowane w moim systemie, więc jestem skłonny powiedzieć, że nie mogę uznać go za „przenośny”. Spróbuję jednak wprowadzić zmiany, które wymieniłeś za kilka minut (odejdę na kilka). Dzięki! :)
Matthieu Cartier
@neurolysis: bcjest tylko kolejną opcją do wykonywania matematyki zmiennoprzecinkowej, która może skutkować krótszymi poleceniami. Uznałem za pewnik, że jest wszechobecny. AWK jest w tym celu w porządku.
Dennis Williamson
Wydaje mi się, że drukuje to nan%. Czy potrafisz to odtworzyć?
Matthieu Cartier
@neurolysis: Która część to robi?
Dennis Williamson
1
@neurolysis: Co robią /bin/df $DEVICEMOUNTPOINTQi co robią /bin/df $ISOMOUNTPOINTQ? Czy to jedna linia nagłówka, po której następuje coś takiego /dev/xxx 1111 2222 3333 44% /mountpoint? Jeśli dodasz echo "[$DEVICEMOUNTPOINTQ] [$ISOMOUNTPOINTQ]"przed komendą AWK, co wyprowadza? Nawiasem mówiąc, mam literówkę w tym fragmencie. Na końcu jest dodatkowy podwójny cytat. Spróbuj to usunąć.
Dennis Williamson,
4

Po pierwsze, będzie to w dużej mierze zależało od rodzaju plików w katalogu źródłowym. Sądzę, że prawdopodobnym winowajcą są rzadkie pliki. Plik rzadki to taki, w którym stat.st_size! = (Stat.st_blksize * stat.st_blocks); to znaczy całkowity rozmiar pliku jest większy niż liczba bloków danych powiązanych z i-węzłem pliku. Wszelkie nieprzydzielone bloki są odczytywane jako blok zer przez wywołania systemowe. Więc jeśli użyjesz cp (1) na pliku rzadkim, plik docelowy będzie zawierał więcej bloków (zawierających tylko zera) niż plik źródłowy. Polecenia du (1) i df (1) sprawdzają liczbę bloków, a nie rozmiar pliku (ów). Pliki podstawowe są często tworzone jako pliki rzadkie, ponieważ mogą wymagać mapowania pamięci. Ten typ pliku jest przydatny do tworzenia obrazów dysków, na przykład do tworzenia wirtualnego dysku hosta o rozmiarze 15 GB. Byłoby bardzo marnotrawstwem przydzielanie wszystkich bloków w czasie tworzenia; rozmiar (st_size) może wynosić 15 GB, ale rzeczywista liczba bloków może zaczynać się od 0.

To tylko jeden typ pliku, który może wybuchnąć po skopiowaniu. Nie wiedząc, co masz w systemie plików, trudno powiedzieć, co jeszcze może to zrobić.

Arcege
źródło
System plików jest dosłownie czysty (skrypt powłoki usuwa również MBR, w tym tablicę partycji i konfiguruje nową partycję NTFS od zera). Dzięki za informację!
Matthieu Cartier
2

Możesz użyć rsync w trybie tylko lokalnym, w którym zarówno źródło, jak i miejsce docelowe nie mają w nazwie znaku „:”, dzięki czemu zachowuje się jak ulepszone polecenie kopiowania. Z parametrem progress wyświetla coś podobnego do tego ( źródło ):

$ 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

Ponieważ nie daje to całkowitego procentu, innym rozwiązaniem może być użycie tego skryptu ( źródła ):

#!/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
}

W akcji:

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

Możesz także przejrzeć pliki przenoszenia z paskiem postępu, które szczegółowo opisują, jak dodać do cp i mv przełącznik -g, aby pokazać postęp.

harrymc
źródło
Zastanawiałem się nad użyciem strace, ale koszty ogólne są śmieszne.
Matthieu Cartier
Nie jestem pewien co do kosztów ogólnych. To fajny scenariusz, może warto spróbować.
harrymc
Po prostu nie jest możliwe użycie czegoś, co wydaje się powodować około 30% spowolnienia kopiowania. Ale dzięki.
Matthieu Cartier
Standardowy wygaszacz twarzy: dlaczego po prostu nie wprowadzić w skrypcie procentu na poziomie 99,9%, dopóki się nie skończy? Nikt nigdy nie dowie się, dlaczego waha się trochę przy 99,9.
harrymc