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 :
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 $@ $< .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