Voila un sujet qui traine dans mes archives depuis un moment et que j’ai enfin pris le temps d’étudier.

Lorsque l’on fait la copie de tout un répertoire en ligne de commande (via cp) contenant pas mal de fichier il est toujours intéressant de savoir où on en est rendu.

J’ai donc cherché comment ajouter une barre de progression lors d’une copie avec cp à l’image de la boite de dialogue de copie de fichier qui apparait sous Gnome/Nautilus.


Je recherche un outil :

  • en ligne de commande
  • permettant de copier tout un répertoire récursivement
  • indiquant la progression totale de la copie (sous forme d’une barre)

et de façon optionnelle indiquant :

  • la progression par fichier
  • la vitesse de copie
  • le fichier copié en cours
  • le temps restant pour le fichier
  • le temps restant global


Pour les divers outils trouvés je les ai testé en copiant un répertoire de 3 949 éléments pour un total de 3,4 Go.

Le répertoire contient un mixte de petits fichiers (répertoire d’installation d’Eclipse) et de gros (plusieurs ISO)

Pour chacun des outils j’ai fait une mesure du temps écoulé (avec la commande time) et voici pour commencer le temps avec la simple commande cp -aR


real    1m55.563s
 
user    0m0.224s     
sys     0m17.285s
CPU     0m17.509s (user + sys)


Je vais maintenant lister les différentes possibilités trouvées.


Fonction cp_p dans le .bashrc

Le première solution trouvée sur « Can you get cp to give a progress bar like wget? » consiste à ajouter une fonction (nommée dans l’exemple cp_p) dans le .bashrc.

Cette fonction utilise l’outil strace pour monitorer l’exécution de cp.


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

Malheureusement ce script ne permet pas de copier un répertoire et même avec un fichier unique il ne fonctionne pas, la barre de progression s’affiche sur une ligne nouvelle à chaque mise à jour :



En creusant un peu, je suis tombé sur cette article Ajouter une barre de progression à ‘cp’ qui utilise le même mécanisme (fonction bashrc utilisant strace)

cp_p()
{
 local params=( "$@" )
 # Le dernier champs est la destination
 unset params[$(( ${#params[@]} - 1 ))]
 # $COLUMNS correspond à la largeur de votre terminal
 # et est mise à jour après chaque commande si l'option
 # checkwinsize est définie ou à la réception du signal WINCH
 kill -s WINCH $$
 [ $COLUMNS -lt 20 ] && (cp -a -- "$@"; return $?)
 lim=$(( $COLUMNS - 10 ))
 # Les "--" sont là pour signifier qu'il n'y a pas d'options
 # sinon, il faut modifier le "du" etc...
 # J'utilise le "-a" pour pouvoir copier par exemple des
 # répertoires sans soucis.
 strace -e write cp -a -- "$@" 2>&1 |
 awk '{
 count += $NF    # rajoute la valeur du dernier champs
 # 10 représente la fréquence d affichage
 if (count % 10 == 0)
 {
 percent = count / total_size * 100
 printf "%3d%% [", percent
 for (i=0;i<=percent*'$lim'/100;i++)
 printf "="
 if (percent<100)
 printf ">"
 for (j=i;j< '$lim';j++)
 printf " "
 printf "]\r"
 }
 }
 END { printf "100\n" }' \
 total_size=$(du -bc "${params[@]}" | awk 'END {print $1}') \
 count=0
}


Et les corrections apportés résolvent les problèmes sus-mentionnés (copie de répertoire, vrai barre de progression)


Critère Résultat
Ligne de commande ok
Copie répertoire ok
Progression globale ok
Ne nécessite pas une installation particulière ok
Progression par fichier ko
Vitesse de copie ko
Fichier en cours de copie ko
Temps restant fichier ko
Temps restant global ko
Temps de copie (réel / CPU) 2min 03s / 48s


Tous les critères de base sont remplis, la copie est un peu plus lente (+7%) et sollicite plus le processeur.

Enfin est à noter que parmi les nombreux essai réalisé de temps en temps la barre de progression n’arrivait pas au bout même lorsque que la copie était finie :



Script Bash

Deuxième solution trouvée ici : Copy file in GNU/Linux with progress bar and rate limiting, il s’agit d’un script bash :

#!/bin/bash 
 
set -e
 
if [ $# -lt 2 ]; then
 echo "cppv - copy files with progress bar and rate limiting ability"
 echo "Usage: cppv source_file[s] destination_file_or_directory"
 echo "No other non-positional command line arguments can be given"
 echo "Always recurses like find"
 echo "You can change copying speed limit on the fly with \"pv -R\" if you find out pv's PID"
 echo "Use FIND_OPTS, PV_OPTS, CPIO_O_OPTS, CPIO_I_OPTS to override arguments to the pipeline parts"
 echo "Examples:"
 echo "    cppv a b # Copy file a to b. Just calls \"pv a > b\""
 echo "    cppv a d/ # Copy file a to d/a. Calls \"find a | cpio -o | pv | (cd d && cpio -i)\""
 echo "    cppv *{01..26}*.mkv /mnt/usb/ # Copy all matching files to /mnt/usb/."
 echo "    cppv dir1 dir2 # duplicate dir1"
 echo "    PV_OPTS=\"-L 1M\" cppv . /tmp/ # Limit copying rate to 1M"
 echo "    cppv /home/vi/bin /tmp/ # Warning: Copy /home/vi/bin to /tmp/home/vi/bin"
 exit 1
fi;
 
true ${CPIO_O_OPTS:="-H newc -0o"}
true ${CPIO_I_OPTS:="-0diu --no-absolute-filenames"}
true ${FIND_OPTS:="-depth -print0"}
 
ARGS=( "$@" );
 
DEST="${ARGS[$#-1]}"
unset ARGS[$#-1];
 
if [[ ( "${1:0:1}" == "-"  && ! -e "$1" ) || ( "${DEST:0:1}" == "-" && ! -e "$DEST" )  ]]; then
 echo "There should not be any command line options. Only file names." >&2
 exit 1;
fi
 
DIRMODE=0
 
if [[ $# > 2 || "${DEST:${#DEST}-1:1}" == "/" || -d $DEST ]]; then
 DIRMODE=1
elif [[ -d "$1" && ! -d "$2" ]]; then
 DIRMODE=1
 mkdir "$DEST";
 DEST=`readlink -f "$DEST"`;
 cd "$1";
 ARGS=(".")
fi
 
if [ $DIRMODE == 0 ]; then
 pv "$1" > "$2" && exit 0;
fi;
 
if [ ! -d "$DEST" ]; then
 echo Not a directory: "$DEST" >&2
 exit 1
fi
 
if [ "${1:0:1}" == "/" ]; then
 echo "Warning: it will do a bit different thing than usual cp" >&2
 echo "    For example, copying $1 to $DEST$1, not to $DEST/`basename $1`" >&2
fi
 
SIZE=`du -sb "${ARGS[@]}" | perl -ne '/^(\d+)/ and $q+=$1; END{print $q}'`
 
find "${ARGS[@]}" $FIND_OPTS | cpio $CPIO_O_OPTS | pv -s $SIZE $PV_OPTS | (cd "$DEST" && cpio $CPIO_I_OPTS)


Ce script utilise les commandes cpio et pv. Cette dernière n’est pas installé par défaut sous Ubuntu il faudra donc le faire :

sudo aptitude install pv



Critère Résultat
Ligne de commande ok
Copie répertoire ok
Progression globale ok
Ne nécessite pas une installation particulière ko
Progression par fichier ko
Vitesse de copie ok
Fichier en cours de copie ko
Temps restant fichier ok
Temps restant global ok
Temps de copie (réel / CPU) 2min 24s / 1min 28s


Dans cet outils j’apprécie particulièrement la présentation des données relativement complètes et compactes, cependant le temps de copie est tout de même considérablement allongé (+25%) ainsi que la charge CPU.

Autre point encore plus gênant le comportement de la copie est différent du cp classique, ainsi la ligne de commande :

./cpp.sh Téléchargements/* Temp/essaiCPP/


Ne copie pas le contenu de Téléchargement dans essaiCPP mais copie le répertoire Téléchargement, et si on avait voulu copier un sous dossier de Téléchargement, cela aurait reproduit l’arborescence, exemple :

./cpp.sh Téléchargements/Images_ISO/Ubuntu/* Temp/essaiCPP/

La copie créerait un répertoire Temp/essaiCPP/Téléchargements/Images_ISO/Ubuntu/


Utilitaire vcp (iinet)

Il s’agit d’un binaire visant à remplacer cp (et non plus un script).

Il n’existe pas de version packagé pour Ubuntu il faut donc le compiler depuis les sources que l’on récupérera ici :

http://members.iinet.net.au/~lynx/vcp/


Installation des dépendances :

sudo aptitude install libncurses5-dev libncursesw5-dev


Téléchargement des sources et décompression :

wget http://members.iinet.net.au/~lynx/vcp/vcp-2.2.tar.gz
tar -xvf vcp-2.2.tar.gz
cd vcp-2.2

Compilation / Installation :

make
sudo make install
sudo make installconf


On trouvera la page man de cet utilitaire ici, j’ai utilisé les options R (récursif) et v (verbose) afin d’obtenir le résultat suivant :

A noter qu’il est possible de prendre ces options par défaut en les paramétrant dans le fichier de configuration /etc/vcp.conf


Critère Résultat
Ligne de commande ok
Copie répertoire ok
Progression globale ok
Ne nécessite pas une installation particulière ko
Progression par fichier ok
Vitesse de copie ok
Fichier en cours de copie ok
Temps restant fichier ok
Temps restant global ko
Temps de copie (réel / CPU) 1min 56s / 23s

On constate que cet utilitaire fourni quasiment tous les indicateurs mentionnées et est extrêmement performant (pas de différences notables avec le cp de base).

Seul point noir, ce logiciel ne semble plus maintenu, la dernière version datant de 2007.


Utilitaire vcp (lynix)

En recherchant des informations sur l’outil précédent je suis tombé sur celui-ci ayant le même nom (vcp), dont le développement semble avoir commencé en 2009-2010.

Tout comme le précédent il faut le compiler.


Téléchargement des sources et décompression :

wget http://github.com/lynix/vcp/tarball/master
tar -xvf lynix*.tar.gz
cd lynix*


Compilation / Installation :

make
sudo make install


Rque : si vous installez ce logiciel après le précédent, il le remplacera, en effet ces deux outils ont le même nom et copie le binaire dans le même répertoire (solution modifier le Makefile, cf en fin d’article)

Ou en moins verbeux :


Critère Résultat
Ligne de commande ok
Copie répertoire ok
Progression globale

ok/ko

Que sur les gros fichiers

Ne nécessite pas une installation particulière ko
Progression par fichier

ok/ko

Que sur les gros fichiers

Vitesse de copie

ok/ko

Que sur les gros fichiers

Fichier en cours de copie ok/ko
Temps restant fichier

ok/ko

Que sur les gros fichiers

Temps restant global ko
Temps de copie (réel / CPU) 2min 02s / 14s

L’interface est brouillonne et ne contient pas toutes les informations.


Utilisation de rsync

Au détour d’un forum j’ai vu que quelqu’un préconisait d’utiliser rsync avec l’option –progress.


Critère Résultat
Ligne de commande ok
Copie répertoire ok
Progression globale ko
Ne nécessite pas une installation particulière ok
Progression par fichier ok
Vitesse de copie ok
Fichier en cours de copie ok
Temps restant fichier ok
Temps restant global ko
Temps de copie (réel / CPU) 2min 29s / 1min 33s


Effectivement rsync répond à pas mal d’indicateur (pas de progression totale), on notera aussi la charge CPU et le temps de copie élevé.


Modification de cp directement

C’est la solution que j’ai envisagé en premier, en effet il s’avère que sur Gentoo il existe une option (-g) à la commande de base cp (ainsi que mv) qui ajoute une barre de progression.

Il existe un bug (n° 64067) dans le launchpad (Ubuntu) mais cela reste un suggestion et Ubuntu attend que cette fonctionnalité soit implémentée dans le upstream (branche commune à toutes les distributions Linux)

Gentoo intégre cette fonctionnalité sous la forme d’un patch du package coreutils qui ajoute l’option, les patchs sont disponibles ici :

http://dev.gentoo.org/~vapier/dist/


Il existe bien sur le net des packages pour Debian (cp and mv with progress bar ou http://www.aichler.net/fileutils/patches/) mais pour la version 6.9 de coreutils de Debian Sarge ou Lenny

Sur Obtenir une progress bar avec les commandes cp et mv, il est mentionné un site (Druuna) où la procédure est expliqué malheureusement le site n’existe plus.


Enfin un script shell est mis a disposition automatisant le processus de patch (téléchargement des sources coreutils, du patch, extraction des archive, patch des sources, compilation et installation des nouveau binaire), cependant ce dernier est pour la version 6.9 de coreutils or sous Ubuntu nous somme en 7.4.

Et malheureusement même en adaptant le script (en indiquant les bonnes archives à télécharger) le script ne fonctionne pas pour la version 7.4


Bref il s’agit d’un sujet à creuser (un jour peut être)


Autres solutions non testées :

Copie avec barre de progression dans un terminal : Copie un seul fichier à la fois

Forum Ubuntu-fr.org / Equivalent de SuperCopier sous Ubuntu ??? : Copie un seul fichier à la fois

A progress bar (gauge box) – Linux Shell Scripting Tutorial – A Beginner’s handbook : Source et destination codés dans le script


Conclusion :

Si vous ne souhaitez pas installer quoi que ce soit l’ajout de la fonction cp_p dans le bashrc est une très bonne solution, si au contraire l’installation d’un autre outil ne vous dérange pas vcp (version iinet) est la meilleures solution.



Sources :

Un cp amélioré: vcp


Annexe : Pour modifier le Makefile des 2 utilitaires vcp pour permettre leur installation simultannée.

Editer le /les fichier Makefile présent dans le répertoire de base des sources des utilitaire puis réaliser la compilation (make et make install)

vcp iinet :

CC = cc
 CFLAGS += -Wall
 PREFIX ?= /usr/local
 OBJS = main.o copyall.o copy.o screen.o log.o path.o misc.o color.o
all: $(OBJS)
 
 $(CC) $(CFLAGS) -o vcp $(OBJS) -lcurses
.c.o:
 
 $(CC) $(CFLAGS) -c -I/usr/include/ -o $@ $&lt;
 .SUFFIXS: .c .o
install:
 
 install -c -m 0555 -o root -g `./group.sh` vcp $(PREFIX)/bin
 
...


vcp lynix :

...
install: vcp
 mkdir -p $(DESTDIR)/bin
 install -m 755 vcp $(DESTDIR)/bin/vcp

Les commentaires sont fermés.