0

I am looking for a shell script that would flatten a given directory structure but ONLY if there is only 1 subfolder in that directory. For instance: the script would flatten this folder:

/folder
    /subfolder
        file1
        file2

into:

/folder
    file1
    file2

but would skip (do nothing) this folder

/folder
    /subfolder1
    /subfolder2 

Thank you so much in advance.

Steve

2
  • I've fixed the markup and formatting of the text of the question. Please modify if I got it wrong.
    – Kusalananda
    Jun 22, 2018 at 12:51
  • This would involve rebuilding relative symlinks, I gather.
    – user147505
    Jun 22, 2018 at 12:56

1 Answer 1

1

A somewhat naive approach:

#!/bin/sh

for dir do

    # get list of directories under the directory $dir
    set -- "$dir"/*/

    # if there are more than one, continue with the next directory
    # also continue if we didn't match anything useful
    if [ "$#" -gt 1 ] || [ ! -d "$1" ]; then
        continue
    fi

    # the pathname of the subdirectory is now in $1

    # move everything from beneath the subdirectory to $dir
    # (this will skip hidden files)
    mv "$1"/* "$dir"

    # remove the subdirectory
    # (this will fail if there were hidden files)
    rmdir "$1"

done

Using bash:

#!/bin/bash

for dir do

    # get list of directories under the directory $dir
    subdirs=( "$dir"/*/ )

    # if there are more than one, continue with the next directory
    # also continue if we didn't match anything useful
    if [ "${#subdirs[@]}" -gt 1 ] || [ ! -d "${subdirs[0]}" ]; then
        continue
    fi

    # the pathname of the subdirectory is now in ${subdirs[0]}

    # move everything from beneath the subdirectory to $dir
    # (this will skip hidden files)
    mv "{subdirs[0]}"/* "$dir"

    # remove the subdirectory
    # (this will fail if there were hidden files)
    rmdir "${subdirs[0]}"

done

Both scripts would be run as

$ ./script.sh dir1 dir2 dir3

or

$ ./script.sh */

to run it across all directories in the current directory.

Apart from the caveats in the code, this would also fail to relink symbolic links. To do that, you would have to go through all possible locations in the filesystem and look for links pointing into the subdirectory under /folder and recreate them so that they point to the correct new place. I will not write the code far that here.

Also, no check is made when moving things out of the subdirectory to make sure that there are no entries with the same names already existing under /folder.

8
  • Thank you. Ill give it a try. How about if I wanted to check all folders in any given directory in a loop like in "for every folder in directory do ...."?
    – Steven D
    Jun 22, 2018 at 15:04
  • @StevenD You wanted to do this only when the directory contained a single subdirectory, right? A loop would not allow you to count the number of subdirectories.
    – Kusalananda
    Jun 22, 2018 at 15:09
  • @StevenD Or I might have misunderstood you... Do you mean that you want to iterate over a number of folders, and flatten each individually if they contained only one directory? I.e. let /folder (in your question) be dynamic?
    – Kusalananda
    Jun 22, 2018 at 15:25
  • Kusalanda. Exactly. Flatten each folder (within a directory) that contains only one subfolder. Doable?
    – Steven D
    Jun 23, 2018 at 15:47
  • @StevenD See updated answer. Basically we do the operation on each directory listed on the command line of the script.
    – Kusalananda
    Jun 23, 2018 at 15:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.