Merge folder contents in OSX/*nix


Merge folder contents in OSX/*nix
#1
After several less than helpful Google searches I've decided to ask my question here where we have a handful of OSX/*nix gurus.

One of the most frustrating things about OSX is the fact that when I drag and drop folder into directory where one of the same name already exists, instead of merging the folder contents like Windows does, it simply replaces the folder outright. This makes upgrading my LDraw installation frustrating unless I simply trash the old ldraw folder and download complete.exe. Is there a way to force OSX to merge folder contents instead of replacing? Barring that is there a command line way of doing this?

Note: I don't want to download and use a separate program to do this as I consider that just as time consuming. I'd rather have a in GUI solution or a command line option that I can write an script to leverage.
Reply
Re: Merge folder contents in OSX/*nix
#2
I don't know if there's a quick simple answer to your question or not. However, I have some answers.

If you unzip a file from the command line, unzip will ask if you want to replace existing files, and one of the responses is to replace all files. The -o command line option should also cause it to overwrite without prompting. Additionally, LDView will automatically download official updates and install them for you, so for LDraw updates of official parts, that's what a lot of people here suggest to new users (which obviously you are not).

In general, you can use the unzip command line behavior to do this for any directory. Right click on the directory in Finder and select the Compress item. This will create a zip file with the same name as the directory. Then go to the parent directory of the destination in a Terminal window, and use unzip -o <zip filename>. This zip stuff should work in just about any *nix, since zip/unzip command line tools are often installed by default.

Also, if you're on the command line anyway, cp -R does a recursive copy, and it should "merge" by default.
Reply
Re: Merge folder contents in OSX/*nix
#3
In proof form:
Quote:Let the desired output of merge(main, new) be defined by the union of the set of all files in main/new and the set of all files in new, where, when these sets intersect, the files in new are assumed to be the desired files.

Let LDraw be a directory arranged like so:
Code:
$ mkdir LDraw
$ mkdir LDraw/parts
$ touch LDraw/parts/A.dat
Let your new folder, parts, be arranged like so:
Code:
$ mkdir parts
$ touch parts/B.dat

Given the above directories, the following copy will "merge" parts into LDraw:
Code:
$ cp -r parts LDraw
Thus arriving at:
Code:
$ ls LDraw/parts
A.dat    B.dat

Q.E.D.

and then rm -r parts if you want to. As far as I am aware, there is no portable way to do this with mv.

AFAIK there isn't some modifier key you can hold down to do what you want (like option to copy, or cmd option to make a symlink (or "alias" as they are called on mac)).

However, if you're only dealing with LDraw folders... I always just select the files themselves, it's really not that big of a deal since we only have 4 folders (or 8 if you count unofficial/*).

Not an elegant solution by any means, it's just what I find most practical.
Reply
Re: Merge folder contents in OSX/*nix
#4
Jean-Philippe Ouellet Wrote:
Code:
$ cp -r parts LDraw

Unfortunately, that solution is insufficiently versatile for my day-to-day needs, in which I frequently need to copymerge the *contents* of one directory into the *contents* of another. Using cp requires that a) the two folders being merged have exactly the same names and b) that you specify the parent of the destination as the destination path. For me, (a) is frequently untrue, and (b) is always inconvenient.

Here is the Perl script I adapted from source I found online. I use it all the time for my purposes:

Code:
usage: mergedirs.pl [--no_overwrite | --verbose] source_path destination_path

It causes all the contents of source_path to be merged into the contents of destination_path. The root directory names need not match. There are global variables within the script to ignore UNIX invisible files, because I need to use this with version control systems.

Code:
#!/usr/bin/perl -w
# -*- Mode: perl; tab-width: 4; indent-tabs-mode: nil; -*-
#
# Directory Merging Script
# Version 1.0
# version 1.1 Allen Smith. added overwriting files and leaving source intact
#
# Copyright (c) 2002 by Ian Hickson
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

use strict;
use File::Copy;

# Global control options
my $verbose                = 0; # print lots of error messages
my $overwriteFiles        = 1; # overwrite files in destination
my $leaveSourceIntact    = 1; # whether to do a copy or a move merge
my $ignoreInvisibles    = 1; # ignore Unix inivisible files (. prefix) in source


# takes two arguments: sourcePath, destinationPath
# recursively descend through sourcePath, moving stuff that doesn't exist in destinationPath into it

my $argumentsAreValid        = 0;

if (@ARGV >= 2)
{
    my $counter = 0;

    # Test for option switches first.
    while( $ARGV[$counter] =~ m/^--/ )
    {
        if( $ARGV[$counter] =~ m/--no_overwrite/ )
        {
            $overwriteFiles = 0;
        }
        elsif( $ARGV[$counter] =~ m/--verbose/ )
        {
            $verbose = 1;
        }
        else
        {
            print STDERR "Unexpected option " . $ARGV[$counter] . "\n";
        }
        
        # advance to the next switch
        $counter += 1;
    }
    
    # The remaining arguments are the paths
    if(@ARGV == $counter + 2)
    {
        $argumentsAreValid = 1;
        
        merge($ARGV[$counter], $ARGV[$counter + 1]);
    }
}

# Invalid arguments. Print an error.
if($argumentsAreValid == 0)
{
    print STDERR "usage: mergedirs.pl [--no_overwrite | --verbose] source_path destination_path\n";
}


#========== merge ==============================================================
#
# Purpose:        Recursively merges the source path into the destination,
#                affected by the global settings flags.
#
# Parameters:    $_[0]    = the source path
#                $_[1]    = the destination path
#
#===============================================================================
sub merge
{
    my($sourcePath, $destinationPath)    = @_;
    my $success = 0;
    
    #    SOURCE: not -e      -f    -d     empty -d else
    # TARGET:
    #  not -e     err      mv    mv      mv      err
    #
    #       -f     err      cp    err      err      err
    #
    #       -d     err      err    loop  rmdir      err
    #
    #     else     err      err    err      err      err
    print "merge: merging $sourcePath and $destinationPath\n" if $verbose;
    
    if( $sourcePath =~ /^\./ and $ignoreInvisibles == 1 )
    {
        print "merge: ignoring invisible $sourcePath\n" if $verbose;
    }
    elsif (not -e $sourcePath)
    {
        print STDERR "merge:$sourcePath: doesn't exist\n";
    }
    elsif (not (-f $sourcePath or -d $sourcePath))
    {
        print STDERR "merge:$sourcePath: not a normal file\n";
    }
    else
    {
        # If current path is a directory, and the destination does not exist, create a
        # new empty destination directory. The recursive calls below will then fill it in.
        if(-d $sourcePath and not -e $destinationPath)
        {
            mkdir($destinationPath);
        }
        
        #---------- Do the real copy now.
        
        # b does not already exist. Just copy the file.
        if(not -e $destinationPath)
        {
            print "merge: moving $sourcePath to $destinationPath\n" if $verbose;
            if($leaveSourceIntact == 1)
            {
                $success = copy($sourcePath, $destinationPath);
            }
            else
            {
                $success = move($sourcePath, $destinationPath);
            }
            
            if($success == 0)
            {
                print STDERR "merge:$sourcePath: could not move to $destinationPath, $!\n";
            }
        }
        # b exists and is a file
        elsif(-f $destinationPath)
        {
            # overwrite existing file in the second directory!
            if($overwriteFiles == 1)
            {
                print "merge: overwriting $destinationPath with $sourcePath\n" if $verbose;
                if($leaveSourceIntact == 1)
                {
                    $success = copy($sourcePath, $destinationPath);
                }
                else
                {
                    $success = move($sourcePath, $destinationPath);
                }
                
                if($success == 0)
                {
                    print STDERR "merge:$sourcePath: could not overwrite to $destinationPath, $!\n";
                }
            }
        }
        # b is a directory
        elsif(-d $destinationPath)
        {
            # make sure a is also a directory
            if (-d $sourcePath)
            {
                my @entries = getdir($sourcePath);
                if (@entries)
                {
                    # not empty
                    # recurse through it to give us a chance to make it empty
                    print "merge: going through contents of $sourcePath\n" if $verbose;
                    foreach my $entry (@entries) {
                        my $c = "$sourcePath/$entry";
                        $c =~ s|//|/|gos;
                        my $d = "$destinationPath/$entry";
                        $d =~ s|//|/|gos;
                        &merge($c, $d);
                    }
                }
                # empty now?
                if(not $leaveSourceIntact)
                {
                    @entries = getdir($sourcePath);
                    if (not @entries)
                    {
                        print "merge: deleting empty directory $sourcePath\n" if $verbose;
                        rmdir($sourcePath) or print STDERR "merge:$sourcePath: could not delete directory, $!\n";
                    } else {
                        print STDERR "merge:$sourcePath: could not delete directory, directory is not empty\n";
                    }
                }
            }
            else {
                print STDERR "merge:$sourcePath: conflicts with directory $destinationPath\n";
            }
        }
        else
        {
            print STDERR "merge:error\n";
        }
    }
}

sub getdir {
    my($sourcePath) = @_;
    local *DIR;
    unless (opendir(DIR, $sourcePath)) {
        print STDERR "merge:$sourcePath: can't open directory\n";
        return;
    }
    my @entries;
    while (my $entry = readdir(DIR)) {
        if ($entry !~ m/^\.\.?$/o) {
            push(@entries, $entry);
        }
    }
    closedir(DIR) or print STDERR "merge:$sourcePath: could not close directory, $!\n";
    return @entries;
}
Reply
Re: Merge folder contents in OSX/*nix
#5
Allen Smith Wrote:
Jean-Philippe Ouellet Wrote:
Code:
$ cp -r parts LDraw

Unfortunately, that solution is insufficiently versatile for my day-to-day needs, in which I frequently need to copymerge the *contents* of one directory into the *contents* of another. Using cp requires that a) the two folders being merged have exactly the same names and b) that you specify the parent of the destination as the destination path. For me, (a) is frequently untrue, and (b) is always inconvenient.

At least on the Mac, this can be avoided by appending / to the source directory name (and not the desination directory name). Like so:

Code:
cp -R NewLDraw/ ~/Libraray/ldraw

The above will merge the NewLDraw directory contents with the ~/Library/ldraw directory contents.
Reply
Re: Merge folder contents in OSX/*nix
#6
Travis Cobbs Wrote:At least on the Mac, this can be avoided by appending / to the source directory name (and not the desination directory name). Like so:

Code:
cp -R NewLDraw/ ~/Libraray/ldraw

The above will merge the NewLDraw directory contents with the ~/Library/ldraw directory contents.

Oh, that is very nice. I wish I had known that years ago.

Allen
Reply
« Next Oldest | Next Newest »



Forum Jump:


Users browsing this thread: 4 Guest(s)