Ajouter une barre de progression à ‘cp’

Si vous vous êtes déjà posé la question de pourquoi diable cp ne propose pas de barre de progression, vous êtes au bon endroit (quoique ça ne doit sûrement pas être le seul) :)

cp lors d’une copie et à moins de lui donner l’option -v est muet comme une tombe, mais il peut être nécessaire ou au minimum rassurant d’afficher une barre de progression surtout quand on a pas d’environnement graphique. Pour cela, il suffit de se servir de strace, awk, du tout ça mélangés dans du bash.

strace permet de suivre tout appel système fait par la commande donnée en paramètre, mais surtout, il permet de filtrer ce qu’on surveille, cp n’étant en fin de compte qu’un programme qui écrit dans un fichier, il faudra surveiller tout appel à write, un exemple:

$ dd if=/dev/zero of=file_test count=100K bs=1
102400+0 enregistrements lus
102400+0 enregistrements écrits
102400 octets (102 kB) copiés, 0,521373 s, 196 kB/s
$ strace -e write cp file_test /dev/null
write(4, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 32768) = 32768
write(4, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 32768) = 32768
write(4, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 32768) = 32768
write(4, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096) = 4096
$

L’important dans la sortie de strace est le dernier chiffre qui correspond au nombre d’octets écrits:
32768 * 3 + 4096 = 102400

Voilà, il n’y a plus qu’à faire une petite fonction qui détecte la taille de ce qu’on veut copier, qui somme les valeurs en fin de ligne et transformer tout ça en barre de progression.

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
}

Une fois la fonction dans le ~/.bashrc:

$ cp_p file_test /dev/null
100% [==============================================]

source.

Commentaires (9)