コピーされたディレクトリがコピー先で大きくなったように見える

コピーされたディレクトリがコピー先で大きくなったように見える

シェル スクリプトの一部として次のコードがあります。

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

cpこれは次の操作を実行している監視です。

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

これはほとんどの場合うまくいきますが、

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

なぜこれがソースのサイズの 100% を超えるのでしょうか? コピーは、ループマウントされた ISO から USB フラッシュ ドライブ上の NTFS 形式のパーティションに行われます。これはおそらくファイル システムの問題だと思います。

私の例では、サイズを一致させて、cp完了時に 103% ではなく 100% コピーされるようにするために何が欠けているのでしょうか?

ありがとう。


Re: 賞金

以下の基準を満たす、上記のコードに類似したソリューションを最初に作成した人に賞金を授与します。

  • スクリプトは1:1の比率でコピーを検出できなければならない
  • スクリプトは100%を超えるコピーされた値を表示してはならない。しかし...
  • スクリプトは、コピーされた量が 100% を超えた場合に、単純に表示を 100% に制限してはなりません。

データサイズがする実際に何らかの理由でコピー元とコピー先で異なる場合は、これを認識してコピーされた実際の比率を表示するスクリプトが必要です。

答え1

コードを簡略化して読みやすくすると次のようになります。

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

最後のawk行は次の 2 つに置き換えることができます。

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

次に、そのステートメントの直前に次の操作を実行しますprintf

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

wait $CPPIDループの終了直後に追加しますwhile。これにより、100% に達すると印刷の進行が停止します。

見るプロセス管理PID の信頼性に関して (リサイクルされます)。

発生している問題は、おそらく、宛先ファイルシステムの「使用済み」値ではなく、違い開始値から現在の値まで。

ループの前に次のような行を追加してみてくださいwhile

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

ループ内の行を次のように変更します。

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

もちろん、rsync --progressの代わりにを使用すれば、これらすべてを回避できる可能性がありますcp

編集:

また、while上記のループでこれを試して、計算に使用されている数値を確認してください。これにより、何が起こっているのかの手がかりが得られるかもしれません。

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

答え2

まず、ソース ディレクトリ内のファイルのタイプに大きく依存すると思います。原因はスパース ファイルである可能性が高いと思います。スパース ファイルとは、stat.st_size != (stat.st_blksize * stat.st_blocks) であるファイルです。つまり、ファイル全体のサイズが、ファイルの inode に関連付けられたデータ ブロックの数よりも大きいファイルです。割り当てられていないブロックは、システム コールによってゼロのブロックとして読み取られます。そのため、スパース ファイルで cp(1) を使用すると、宛先ファイルにはソース ファイルよりも多くのブロック (ゼロのみを含む) が含まれます。du(1) コマンドと df(1) コマンドは、ファイルのサイズではなく、ブロックの数を参照します。コア ファイルは、メモリをマップする必要がある場合があるため、スパース ファイルとして作成されることがよくあります。このタイプのファイルは、ディスク イメージの作成に役立ちます。たとえば、サイズが 15 GB の仮想ホストのドライブを作成する場合などです。作成時にすべてのブロックを割り当てるのは非常に無駄です。サイズ (st_size) は 15GB になる場合がありますが、実際のブロック数は 0 から始まる可能性があります。

これは、コピーすると爆発する可能性のあるファイルの 1 つのタイプにすぎません。ファイルシステムに何があるかがわからないため、他に何が爆発を引き起こしているかを判断するのは困難です。

答え3

rsync をローカルのみのモードで使用することもできます。このモードでは、ソースと宛先の両方の名前に ':' が含まれないため、改良されたコピーコマンドのように動作します。progress パラメータを使用すると、次のようなものが表示されます (ソース) :

$ 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

これでは合計パーセンテージが得られないので、別の解決策としてこのスクリプトを使用することもできます(ソース) :

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

実際のところ:

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

こちらもご覧ください進行状況バー付きでファイルを移動する進行状況を表示するために cp および mv に -g スイッチを追加する方法を詳しく説明します。

関連情報