2

Lets say that I have the following directory tree structure:

./Original
  ├── Dir1
  │   ├── Objects
  │   └── Textures
  ├── Dir2
  │   ├── SubDir1
  │   │   └── SubSubDir1
  │   │       ├── Objects
  │   │       └── Textures
  │   └── SubDir2
  │       ├── Objects
  │       └── Textures
  └── Dir3
      ├── Objects
      ├── SubDir1
      │   ├── Objects
      │   └── Textures
      └── Textures

And I also have a similar structure (it is actually a copy of the Original Directory without the Objects and Textures directories:

./Copy
  ├── Dir1
  ├── Dir2
  │   ├── SubDir1
  │   │   └── SubSubDir1
  │   └── SubDir2
  └── Dir3
      └── SubDir1

How do I move all of the Objects and Textures directories and their files from the original to the corresponding position in the Copy Directory? It should look like this when complete:

./Original
  ├── Dir1
  ├── Dir2
  │   ├── SubDir1
  │   │   └── SubSubDir1
  │   └── SubDir2
  └── Dir3
      └── SubDir1

./Copy
  ├── Dir1
  │   ├── Objects
  │   └── Textures
  ├── Dir2
  │   ├── SubDir1
  │   │   └── SubSubDir1
  │   │       ├── Objects
  │   │       └── Textures
  │   └── SubDir2
  │       ├── Objects
  │       └── Textures
  └── Dir3
      ├── Objects
      ├── SubDir1
      │   ├── Objects
      │   └── Textures
      └── Textures

Things to notice:

  • Not all directories in the Original Directory has Objects and Textures directories
  • The Objects and Textures Directories are moved and not copied
  • Also the Original and Copy directories are in the same directory and this is where the command/function should be called

Please use this command to build the starting structure:

mkdir -p Original/Dir1/Objects Original/Dir1/Textures Original/Dir2/SubDir1/SubSubDir1/Objects Original/Dir2/SubDir1/SubSubDir1/Textures Original/Dir2/SubDir2/Objects Original/Dir2/SubDir2/Textures Original/Dir3/Objects Original/Dir3/Textures Original/Dir3/SubDir1/Objects Original/Dir3/SubDir1/Textures
mkdir -p Copy/Dir1 Copy/Dir2/SubDir1/SubSubDir1 Copy/Dir2/SubDir2 Copy/Dir3/SubDir1
11
  • I can not really understand what you want exactly. either use find or rsync.
    – Panther
    Oct 5, 2017 at 17:49
  • An assignment for sure, so tell me what have you tried so far? Oct 5, 2017 at 17:49
  • It's not an assignment. I'm messing Aaron's with my files as bash Oct 5, 2017 at 17:51
  • 2
    @George I gave you the mkdir so you can build a test environment for yourself. My actual directories are not nabbed Dir and SubDir Oct 5, 2017 at 17:59
  • 2
    The point is that the directory name doesn't matter. That way if someone wants to use it they don't have to change much Oct 5, 2017 at 18:24

4 Answers 4

4

This is a simulation. Once you've run it and you're sure everything's correct, remove echo and run it again:

shopt -s globstar
for d in **/{Objects,Textures}; do
    echo mv "$d" "Copy/${d##Original/}"
done

Explanation:

  • Turning on the Bash option "globstar" makes the glob **/ match zero or more subdirectories.
  • Bash expands **/{Objects,Textures} to **/Objects **/Textures.
  • ${d##Original/} means "remove the string Original/ from the start of $d.
2

If you want to update directory trees using another location, the best tool is rsync, the the second-best possibly tar. Writing a shell script is often overkill:

rsync -aP --ignore-existing Original/ Copy

The options:

  • -a tells rsync to retain ownerships, modes, etc.
  • -P prints progress information
  • --ignore-existing means that files already in Copy won't be touched.
  • The trailing / after Original tells rsync to copy the contents of Original to Copy, instead of copying Original itself.

Delete Original when done, or use the --remove-source-files files option to have rsync delete files when it's done copying them.

1

Here's one way you could manage to move directories with no subdirectories.

First turn on recursive globbing:

shopt -s globstar

Now you can glob recursively with **. You can turn it off with shopt -u globstar, but it will be off anyway next time you open a shell.

Now in the parent directory of both Copy and Original:

for d in Original/**/; do $(ls -lAq "$d" | grep -q '^d') || echo mv -vn -- "$d" Copy"${d/Original/}"; done

Remove echo after testing to actually move the directories (and their contents of course - mv doesn't have an -r flag)

Let's make that a bit more readable

shopt -s globstar
for d in Original/**/; do 
    $(ls -lAq "$d" | grep -q '^d') || 
    echo mv -vn -- "$d" Copy"${d/Original/}"
done

Wait, did I just parse the output of ls? Uh, yes, I did. But I think it's alright(!) since I think I can rely on the output's consistency in this case. No matter what characters a filename contains, the output of ls -l on the directory where that file resides will begin with a string that defines its type and permissions. In ls -l all directory listings begin with a d, eg

drwxrwxr-x 26 zanna zanna 4096 Oct  5 19:57 playground

Even newlines don't mess this up, as they are displayed as ? (even after piping) if we add -q.

$(ls -lAq "$d" | grep -q '^d') 

checks that the directory has some subdirectories. Instead of collecting output from grep, we collect the exit status. If the command is successful, we do nothing, but if it fails, we mv the directory (using the || or operator, which means, if the command before || fails, do the one after it).

We use string manipulation to remove Original so the directories are copied to the right places in Copy.

Here's the result:

$ tree
.
├── Copy
│   ├── Dir1
│   │   ├── Objects
│   │   └── Textures
│   ├── Dir2
│   │   ├── SubDir1
│   │   │   └── SubSubDir1
│   │   │       ├── Objects
│   │   │       └── Textures
│   │   └── SubDir2
│   │       ├── Objects
│   │       └── Textures
│   └── Dir3
│       ├── Objects
│       ├── SubDir1
│       │   ├── Objects
│       │   └── Textures
│       └── Textures
└── Original
    ├── Dir1
    ├── Dir2
    │   ├── SubDir1
    │   │   └── SubSubDir1
    │   └── SubDir2
    └── Dir3
        └── SubDir1
0

Here's an example of how this might work for you. Please note that this script is to be run at the same level with the original and copy directories and also that it probably needs adaptation to your actual needs. Good Luck.

#!/bin/bash

src="original" 
dst="./copy"

function findLocation {
    for i in `find $src/ -type d -name "$1"`; do
        IFS='/' read -ra directories <<< "$i"
        dirNum=${#directories[@]}
        let "arrayNum=$dirNum-2"

        arrs=()

        for ((k=1 ; k<=$arrayNum; k++)); do
            arrs+=("${directories[$k]}")
        done

        echo "===============================++++++++++++++++"

        function join {
            local IFS="$1"
            shift
            echo "$*"
        }

        constructDst=`join / ${arrs[@]}`
        destination="$dst/$constructDst/"

        if [ ! -d $destination ] ; then
            mkdir -p $destination
            echo "creating directory: $destination"
        fi

        if [ -d $i ]; then
            echo "Moving $i to $destination"
            mv $i -t $destination
        fi
    done
}

findLocation Textures 
findLocation Objects

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .