Last active
December 26, 2015 06:59
-
-
Save mx-moth/7112049 to your computer and use it in GitHub Desktop.
A bash script to copy music to my phone. The phone/mtpfs has issues staying connected, and file transfer speed is not great, so I did not want to overwrite existing files. MTP does not support renaming files, so `rsync` did not work. Finally, FAT does not support some characters in filenames (sorry, GY!BE, "Motherfucker=redeemer" does not work),…
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# A dodgy ripoff of rsync for my music files | |
# | |
# Usage: To copy the contents of "Zechs Marquise/Getting Paid/" and "Grails/" | |
# from your music library to your mounted phone: | |
# | |
# syncdir --source $HOME/Music \ | |
# --destination /mnt/phone/Music \ | |
# "Zechs Marquise/Getting Paid/" \ | |
# "Grails/" | |
# | |
# The source defaults to `pwd`. This script works best when you are in the | |
# source directory, as you can leave off the source and tab-complete files to | |
# copy: | |
# | |
# cd $HOME/Music | |
# syncdir --destination /mnt/phone/Music \ | |
# "Zechs<TAB>..." \ | |
# "Gr<TAB>" | |
# | |
# To send your whole music library across, use it like so: | |
# | |
# cd $HOME/Music | |
# syncdir --destination /mnt/phone/Music | |
# | |
# or: | |
# | |
# syncdir --source $HOME/Music --destination /mnt/phone/Music | |
# | |
# This never deletes files, and should not be used for transferring back to | |
# your music library. It is designed specifically for transferring to | |
# intermittent FAT devices. | |
args=$( getopt \ | |
--options t:f:vqs \ | |
--longoptions destination:,source:,silent \ | |
--name 'sync-music' \ | |
-- "$@" ) | |
if [ $? -ne 0 ] ; then | |
exit 1 | |
fi | |
eval set -- "$args" | |
dst_base= | |
src_base=$( pwd ) | |
log_level=2 | |
while true ; do case "$1" in | |
-f|--source) | |
src_base="$2" | |
shift 2 | |
;; | |
-t|--destination) | |
dst_base="$2" | |
shift 2 | |
;; | |
-v) # Increase verbosity | |
log_level=$(( $log_level + 1 )) | |
shift | |
;; | |
-q) # Decrease verbosity | |
log_level=$(( $log_level - 1 )) | |
shift | |
;; | |
--silent) # Turn off output | |
log_level=0 | |
shift | |
;; | |
--) | |
shift | |
break | |
esac ; done | |
munge_for_fat() { | |
echo "$1" \ | |
| sed -e 's![[:cntrl:]*/:<>?\\|+,.\;\=\[\]]\+!*!g' \ | |
-e 's!\([/ ]\|^\)[*.]\+!\1!g' \ | |
-e 's![*.]\+\($\|[/ ]\)!\1!g' \ | |
-e 's![*]!-!g' \ | |
| iconv -cs -f UTF-8 -t ASCII | |
} | |
skip_file() { | |
# Check if a source file needs copying to the destination | |
src="$1" | |
dst="$2" | |
if ! [ -e "$dst" ] ; then | |
# The destination file does not exist | |
return 1 | |
fi | |
src_size=$( stat -Lc %s "$src" ) | |
dst_size=$( stat -Lc %s "$dst" ) | |
if [ "$src_size" -ne "$dst_size" ] ; then | |
# The files differ in size | |
return 2 | |
fi | |
# Modification times are not to be trusted, as FAT does not have good | |
# modification timestamp support, so we can not use them as a final test | |
# ala rsync. See the discussion of `--modify-window` in `man rsync` for | |
# more information. | |
return 0 | |
} | |
error() { | |
# Print an error to stderr | |
log error "$@" >&2 | |
} | |
log() { | |
# Usage: `log (info|success|warning|error) message` | |
level="$1" | |
shift | |
case "$level" in | |
info) | |
[ "$log_level" -lt 3 ] && return | |
echo -en '\e[36m' ;; | |
success) | |
[ "$log_level" -lt 2 ] && return | |
echo -en '\e[32m' ;; | |
warning) | |
[ "$log_level" -lt 2 ] && return | |
echo -en '\e[33m' ;; | |
error) | |
[ "$log_level" -lt 1 ] && return | |
echo -en '\e[31m' ;; | |
*) | |
return | |
esac | |
echo -en "$@" | |
echo -e '\e[0m' | |
} | |
while IFS= read -d $'\0' -r src_file ; do | |
dst_file=$( munge_for_fat "$src_file" ) | |
src="$src_base/$src_file" | |
dst="$dst_base/$dst_file" | |
if [ -d "$src" ] ; then | |
# source is a directory | |
if ! [ -e "$dst" ] ; then | |
mkdir -p "$dst" \ | |
&& log "success" "A $dst_file" | |
elif ! [ -d "$dst" ] ; then | |
error "$dst exists and is not a directory" | |
fi | |
else | |
# source is a file | |
skip_file "$src" "$dst" | |
exit_code=$? | |
if [ $exit_code -eq 0 ] ; then | |
# Nothing to do | |
log info " $dst_file" | |
else | |
# Copy the file over, and log if it is an update or modification | |
case $exit_code in | |
1) char="A" ;; | |
2) char="U" ;; | |
esac | |
cp --no-preserve="mode,ownership" --remove-destination "$src" "$dst" \ | |
&& log success "$char $dst_file" | |
fi | |
fi | |
done < <( cd "$src_base" ; find "$@" -print0 ) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment