From 677dbeb9bfab08b8e19e88153ed7b524d8703108 Mon Sep 17 00:00:00 2001 From: Renato Alves <alves.rjc@gmail.com> Date: Tue, 20 Feb 2018 17:36:05 +0100 Subject: [PATCH] ENH Add 'mv_keep_parents' --- README.rst | 2 + bin/mv_keep_parents | 127 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100755 bin/mv_keep_parents diff --git a/README.rst b/README.rst index 3143042..e15f6ce 100644 --- a/README.rst +++ b/README.rst @@ -17,6 +17,8 @@ EMBL utilities ``qs`` - collect queue status across different SGE clusters/queues. Requires key-based authentication with SSH. +``mv_keep_parents`` - equivalent to ``cp --parents src/*.txt dest/`` but moving instead of copying. Optionally keeping source directories if empty with option ``-k``. + Examples ======== diff --git a/bin/mv_keep_parents b/bin/mv_keep_parents new file mode 100755 index 0000000..82a6f36 --- /dev/null +++ b/bin/mv_keep_parents @@ -0,0 +1,127 @@ +#!/usr/bin/env bash + +ERROR="[31;1mERROR:[0m" +KEEPDIRS=0 +VERBOSE=0 +DRYRUN="" + +usage() { + echo >&2 "" + echo >&2 "Usage:" + echo >&2 " $0 [-k] source1 [source2 [...]] destination" + echo >&2 "" + echo >&2 "Will move files from all specified sources to destination" + echo >&2 "while preserving the folder structure of each source" + echo >&2 "" + echo >&2 "Options:" + echo >&2 " -k --keepdirs = keep source directories even if empty. Defaults to removing if empty" + echo >&2 " -d --dryrun = simulate execution without performing actions on the filesystem" + echo >&2 " echoes most actions that would be performed" + echo >&2 " removal of empty directories isn't simulated" + echo >&2 " -v --verbose = echo verbose info while executing. Defaults to non-verbose execution" + echo >&2 "" + echo >&2 "Example:" + echo >&2 " $0 path/to/*.txt destination -> destination/path/to/*.txt" + echo >&2 "" +} + +move_preserve_path() { + parent="$(dirname "$1")" + + $DRYRUN mkdir -p "$2/$parent" + $DRYRUN mv "$1" "$2/$parent" + + if [ "$3" -eq 0 ]; then + empty_dir_remove_recurse "$parent" + fi +} + +empty_dir_remove_recurse() { + # If the dir to remove is the current, don't even try + if [ "$1" == "." ] || [ "$1" == "$(pwd)" ]; then + return 0 + fi + + # Remove original directory if empty (recurses path backwards) + if [ -z "$(ls -A "$1")" ]; then + $DRYRUN rmdir "$1" + + empty_dir_remove_recurse "$(dirname "$1")" + fi +} + +generic_error() { + usage + echo >&2 "${ERROR} $1" + echo >&2 "" + exit 1 +} + +ARG_PARSE="getopt -o vkdh -l verbose,keepdirs,dryrun,help -n $0 --" + +# We process arguments twice to handle any argument parsing error: +ARG_ERROR=$($ARG_PARSE "$@" 2>&1 1>/dev/null) + +if [ $? -ne 0 ]; then + generic_error "$ARG_ERROR" +fi + +# Abort on any errors from this point onwards +set -euo pipefail + +# Parse args using getopt (instead of getopts) to allow arguments before options +ARGS=$($ARG_PARSE "$@") + +# reorganize arguments as returned by getopt +eval set -- "$ARGS" + +while true; do + case "$1" in + # Shift before to throw away option + # Shift after if option has a required positional argument + -v|--verbose) + shift + VERBOSE=1 + ;; + -k|--keepdirs) + shift + KEEPDIRS=1 + ;; + -d|--dryrun) + shift + DRYRUN="echo" + ;; + -h|--help) + shift + usage + exit 1 + ;; + --) + shift + break + ;; + esac +done + +if [ "$#" -lt 2 ]; then + generic_error "$0 requires at least 2 arguments - source and destination" +fi + +# enable verbose execution +if [ "$VERBOSE" -eq 1 ]; then + set -euxo pipefail +fi + +DEST="${!#}" + +if [ ! -d "$DEST" ]; then + generic_error "Destination $DEST doesn't exist" +fi + +while [ "$#" -ge 2 ]; do + SRC="$1" + shift + move_preserve_path "$SRC" "$DEST" "$KEEPDIRS" +done + +# vim: ai sts=4 et sw=4 -- GitLab