Peter’s blog ✴ Week 203 ✴ 6 February 2023

THE WEEKLY CHALLENGE
Quads and directory enquiries

The Perl Camel

Task 2

Copy folders

We are given paths to two folders, $source and $target, and are asked to write a script that recursively copies the directories in $source to $target, without copying any contained files.

Examples


Example 1
Input:  = '/a/b/c' and  = '/x/y'
Source directory structure:
├── a
│   └── b
│       └── c
│           ├── 1
│           │   └── 1.txt
│           ├── 2
│           │   └── 2.txt
│           ├── 3
│           │   └── 3.txt
│           ├── 4
│           └── 5
│               └── 5.txt
Target directory structure:
├── x
│   └── y
Expected Result:
├── x
│   └── y
|       ├── 1
│       ├── 2
│       ├── 3
│       ├── 4
│       └── 5

Analysis

My script assumes that $source and $target exist - I have created them in the preamble - and my script recurses.

My sub copy_dirs($source, $target) creates a like-named sub-directory in $target for any that exist in $source, and if any of these contain subdirectories it recurses with copy_dirs("$source/$subdir", "$target/$subdir").

It would be very easy - one extra line - to copy the contained files as well.

Perl Weekly’s review

from PW issue 603

Practical discussion around the task makes it easy to follow the solution. Well done.

Script


#!/usr/bin/perl

# Peter Campbell Smith - 2023-02-06, revised 2024-11-14, 2026-06-17

use v5.28;
use utf8;
use warnings;

# Blog: http://ccgi.campbellsmiths.force9.co.uk/challenge

# clear out previous runs
system 'rm -r /tmp/from' if -e '/tmp/from';
system 'rm -r /tmp/to' if -e '/tmp/to';

# create roots of from and to
mkdir '/tmp/from';
mkdir '/tmp/to';

# create 'from' structure
chdir '/tmp/from';
mkdir 'apple';
mkdir 'banana';
mkdir 'banana/cherry';
mkdir 'banana/cherry/date';
mkdir 'banana/cherry/orange';
mkdir 'banana/pear';

# now do what was asked
my ($input, $output);

copy_dirs('/tmp/from', '/tmp/to', '');
say qq[\nInput:\n$input\nOutput:\n$output];

sub copy_dirs {
    
    my ($source, $target, @files, $f, $dashes);
    ($source, $target, $dashes) = @_;
    
    # read the contents of $source
    chdir $source;
    opendir(DIR, '.') or die qq[cannot open $source $!];
    @files = sort readdir DIR;
    closedir DIR;
    
    # if there are dirs other than . and .. create them in target
    for $f (@files) {
        next if $f =~ m|^\.\.?$|;   # ignore . and ..
        $input .= qq[$dashes$f\n];
        next unless -d qq[$source/$f];
        mkdir qq[$target/$f] or die qq[cannot make dir $target/$f $!];
        $output .= qq[$dashes$f\n];
        
        # recurse to create any subfolders
        copy_dirs (qq[$source/$f], qq[$target/$f], $dashes . '--');
    }
}

14 lines of code

Output from script


Input:
apple
banana
--cherry
----date
----orange
--pear

Output:
apple
banana
--cherry
----date
----orange
--pear

 

Any content of this website which has been created by Peter Campbell Smith is in the public domain