#!/bin/bash # Title: git-sync.sh # Version: 2.2 # Author: Frédéric CHEVALIER # Created in: 2014-09-03 # Modified in: 2016-01-07 # Licence: GPL v3 #======# # Aims # #======# aim="Synchronize files from a git server on this terminal." #==========# # Versions # #==========# # v2.2 - 2016-01-07: process of list file improved (tmp list not needed anymore) / Timestamp added to the downloaded file to avoid interference with several script instance running at the same time # v2.1 - 2015-03-12: evaluation of the variables created from the list in order to use environment variables (ie, $HOME) / directoy checked instead of created otherwise may lead to unwanted path if error in list file / usage message updated # v2.0 - 2015-02-27: error function improved / info function added / file installed and directory created if not already present / modification of file permission can be specified in the file list and apply to the file # v1.3 - 2014-12-04: warning and error functions improved # v1.2 - 2014-09-24: temp file bug corrected / check if local file exist # v1.1 - 2014-09-14: test_dep bug corrected # v1.0 - 2014-09-14: git dependency check replaced by wget check (bug) / test_dep function improved / remove comments in list file / check for http path / remove spaces in path / checksum loop corrected # v0.1 - 2014-09-23: dry-run option added / no check certifcate option added for wget # v0.0 - 2014-09-03: creation #===========# # Functions # #===========# # Dependency test function test_dep { which $1 &> /dev/null if [[ $? != 0 ]] then error "Package $1 is needed. Exiting..." 1 fi } # Usage message function usage { echo -e " \e[32m${0##*/}\e[00m -l|--list file -d|--dry-run -h|--help Aim: $aim Options: -l, --list Text file containing both remote and local path of a given file for synchronization. - The list file must have a unique path pair per line and each path must be semi-colon separated (remote_path ; local_path). The \e[33mremote path\e[0m must be \e[33mhttp\e[0m a path. - Modification of file permission can be specified in a third column. The syntax is the chmod syntax (ie, remote_path ; local_path ; uog+rwx). - Comments beginning by # are allowed anywhere in the list file. -d, --dry-run Run the synchronization but print a message instead of updating the files. -h, --help This message " } # Error message function error { if [[ -t 1 ]] then echo -e "\e[31mError:\e[00m $1" else echo -e "Error: $1" fi exit $2 } # Warning message function warning { if [[ -t 1 ]] then echo -e "\e[33mWarning:\e[00m $1" else echo -e "Warning: $1" fi } # Info message function info { if [[ -t 1 ]] then echo -e "\e[32mInfo:\e[00m $1" else echo -e "Info: $1" fi } #==============# # Dependencies # #==============# test_dep wget #==========================# # Declaration of variables # #==========================# # Options while [[ $# -gt 0 ]] do case $1 in -l|--list ) list="$2" ; shift 2 ;; -d|--dry-run ) dry=1 ; shift ;; -h|--help ) usage ; exit 0 ;; * ) error "Invalid option: $1\n$(usage)\n" 1 ;; esac done # Check the existence of obligatory options if [[ -z "$list" ]] then error "The option -l is required. Exiting...\n$(usage)\n" 1 fi # Timestamp myts=$(date +%N) #============# # Processing # #============# # Check file list if [[ $(awk -F ";" "END {print NF}" "$list") -gt 3 ]] then error "The list file "$list" is malformated. Each path pair must be on unique line and each path must be semi-colon (';') separated." 1 fi # Synchronization while read line do # Remove comments from list file line=$(echo "$line" | sed "/^\s*#/d;s/\s*#.*$//") # Skip if empty line if [[ -z "$line" ]] then continue fi # Get paths eval mysrc=$(echo "$line" | cut -d ";" -f 1 | sed -e "s/^ *//" -e "s/ *$//") eval mydst=$(echo "$line" | cut -d ";" -f 2 | sed -e "s/^ *//" -e "s/ *$//") eval mymod=$(echo "$line" | cut -d ";" -f 3 | sed -e "s/^ *//" -e "s/ *$//") mydst_tmp="/tmp/${mydst##*/}-$myts" # Check if mydst variable is empty or contain directory if [[ -z "$mydst" ]] then warning "The destination name is absent. Skipping..." continue elif [[ $(echo "$mydst" | grep "/") && ! -d "${mydst%/*}" ]] then warning "${mydst##*/}: ${mydst%/*} directory does not exist. Skipping..." continue fi # Check if remote path is http if [[ ! $(echo "$mysrc" | grep -i "^http") ]] then warning "${mydst##*/}: Remote path is not http address. Skipping..." continue fi # Download file wget --no-check-certificate -O "$mydst_tmp" "$mysrc" &> /dev/null # Check for errors if [[ $? != 0 ]] then warning "${mydst##*/}: An error occured during the downloading. Skipping..." continue # Check if destination file exists elif [[ ! -e "$mydst" ]] then if [[ $dry == 1 ]] then info "${mydst##*/}: file available but not installed." else mv "$mydst_tmp" "$mydst" info "${mydst##*/} installed." fi else # Compare downloaded file and destination file (update file if need) mydst_tmp_md=$(md5sum "$mydst_tmp" | cut -d " " -f 1) mydst_md=$(md5sum "$mydst" | cut -d " " -f 1) if [[ "$mydst_tmp_md" != "$mydst_md" ]] then if [[ $dry == 1 ]] then info "${mydst##*/}: new version available." else mv "$mydst_tmp" "$mydst" info "${mydst##*/} updated." fi else rm "$mydst_tmp" fi fi if [[ -z $dry && -n "$mymod" && -n $(echo "$mymod" | sed -n "/[u,o,g,][+,-][r,w,x,s,t,S,T]/p") ]] then chmod "$mymod" "$mydst" elif [[ -z $dry && -n "$mymod" && -z $(echo "$mymod" | sed -n "/[u,o,g,][+,-][r,w,x,s,t,S,T]/p") ]] then warning "${mydst##*/}: Impossible to change permission, \"$mymod\" does not follow the syntax of the chmod command (see help)." continue fi done < "$list" exit 0