Shell – Make Directory Copies Using Find Command

cpdirectoryfindshell

I have a directory with a bunch of subdirectories in it. Thus

/usr/local/src/ccl/ccl-1.8/x86-headers$ ls
elf  gl  gmp  gnome2  gtk2  jni  libc

Each of these directories has a further subdirectory C inside it, which contains a file populate.sh. I want to create a parallel group of subdirectories with the same structure, but with a random value appended to the directory name (the random value should be the same in all cases), and only containing the C subdirectory with the populate.sh file. These directories contain other files besides the populate.sh file.

This is for a makefile, so for simplicity should probably use standard unix utilties. I'm thinking find with the -exec flag, or possibly xargs would work, but I'm having trouble making sense of the documentation, and I have little experience with shell scripting. Perl might work, but I have not used it, and would prefer not to use it here.

I've been using something like mktemp -u --tmpdir=. to generate a random string in the past, but it is hardly ideal, so I'm open to other suggestions. Ideally I'd like a name that looks like libc.tmp_xw3st. Ie. tmp_ followed by a 5 digit alphanumeric string.

So far, I've got a way of getting a listing of the top level directories. 🙂

find . -maxdepth 1 -type d -print0

../gmp./jni./gl./elf./libc./gtk2./gnome2

A fuller directory listing is at the end of this posting. To summarize, I want to create additional directories like x86-headers/libc.tmpvalue, which only contains the further file x86-headers/libc.tmpvalue/C/populate.sh.

A sketch of a possible approach is to handle this in two steps as follows:

Step 1: Run over the list of top level directories using find, and create a corresponding directory structure eg dirname.tmpvalue/C/ using exec or piping to xargs and using mkdir -p.

Step 2: Run over the list of top level directories again and cp populate.sh into the C subdirectories. This is a bit sloppy, because the list of directories in theory could have altered between the two invocations of find, but this is not an issue in this case.

/usr/local/src/ccl/ccl-1.8/x86-headers$ ls -laR

[...]

./jni:
total 96
drwxr-sr-x 3 faheem staff  4096 Jul 31 00:53 .
drwxr-sr-x 9 faheem staff  4096 Jul 31 00:53 ..
drwxr-sr-x 2 faheem staff  4096 Jul 31 00:53 C
-rw-r--r-- 1 faheem staff 19535 Jul 31 00:53 constants.cdb
[more .cdb files]

./jni/C:
total 12
drwxr-sr-x 2 faheem staff 4096 Jul 31 00:53 .
drwxr-sr-x 3 faheem staff 4096 Jul 31 00:53 ..
-rw-r--r-- 1 faheem staff  148 Jul 31 00:53 populate.sh

./libc:
total 1276
drwxr-sr-x 3 faheem staff   4096 Jul 31 00:53 .
drwxr-sr-x 9 faheem staff   4096 Jul 31 00:53 ..
drwxr-sr-x 2 faheem staff   4096 Jul 31 00:53 C
-rw-r--r-- 1 faheem staff 593125 Jul 31 00:53 constants.cdb
[more .cdb files]

./libc/C:
total 20
drwxr-sr-x 2 faheem staff  4096 Jul 31 00:53 .
drwxr-sr-x 3 faheem staff  4096 Jul 31 00:53 ..
-rwxr-xr-x 1 faheem staff 10544 Jul 31 00:53 populate.sh

Best Answer

This is not a job for find, since there is no recursion involved.

for x in */C/populate.sh; do
  mkdir -- "${x%%/*}$suffix"
  mkdir -- "${x%%/*}$suffix/C"
  cp -p -- "$x" "./${x%%/*}$suffix/C"
done

Remove the -p option to cp if you don't want to preserve the files' modification time.

To generate a random suffix, BSD/Linux mktemp is as portable as it gets.

suffix=$(mktemp -u tmp_XXXXX)

If you want something vaguely random-looking and POSIX-compliant, this gives a string that changes every second and varies from location to location; you can't really do better with only POSIX tools:

suffix=$({ hostname; pwd; date; } |
         cksum | uuencode -m /dev/stdin | awk 'NR==2 {print substr($0,3,5)}')

If you put this code in a makefile, remember to:

  • double all $ signs;
  • put all the code on one line, using ; instead to separate shell instructions (you can use backslash+newline+tab to put a line break in the makefile, but that sequence is removed to build the shell command);
  • start the shell snippet with set -e, so that it aborts if there is any error.
Related Question