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="ERROR:"
+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