Score:9

Move duplicated folder name up one level

nr flag

I have a folder structure:

/mnt/data/mydata/mydata/several-files-and-folders-here

I would like it to instead be:

/mnt/data/mydata/several-files-and-folders-here

I tried mv but it complains the folder isn't empty, and I wasn't able to find a feasible fast solution that I understood, I'm still new to learning Linux. How would one fix this correctly?

Score:11
fr flag

Moving every file in the subdirectory into the parent is one way, but if there are a huge number of entries in /mnt/data/mydata/mydata/, and that's the only entry in /mnt/data/mydata, you might consider this sequence of 3 metadata operations that doesn't change the contents of the big directory, just moves it to a different place in the filesystem. And removes 1 entry from a small directory then unlinks it.

Thus it won't affect the ctime of any of the files in the subdirectory or even have to list the contents of that big directory.

 mv  /mnt/data/mydata            /mnt/data/mydata.old   # rename parent
 mv  /mnt/data/mydata.old/mydata /mnt/data/mydata   # subdir -> sibling of parent
 rmdir  /mnt/data/mydata.old     # fails if not empty

The directory inode that was originally /mnt/data/mydata/mydata is now /mnt/data/mydata, and its contents haven't changed. If it contained millions of files, that's faster and takes less i/o. But more typing because mv won't invent a temporary name for you.

If the original /mnt/data/mydata wasn't empty, you can still do this but instead of rmdir, mv /mnt/data/mydata.old/* /mnt/data/mydata/. If it had a couple entries vs. a couple million for the child directory, that's saving some I/O.

You do need write access to /mnt/data, which the other way doesn't need. But this doesn't need write access to /mnt/data/mydata/mydata.

Script this however you like, and/or cd and use relative paths. Pick any temporary name you want, like foo or tmp, or xyz123. (If other users can write /mnt/data, and you're scripting it, check on what mv will do if they create a file with the same random name you pick; or use mktemp -p /mnt/data/ to get a temp dir there to move the child mydata into while you unlink /mnt/data/mydata.)

It has the downside that there is a moment between operations when no /mnt/data/mydata exists at all. Unless there's a shell wrapper for the renameat2 system call with RENAME_EXCHANGE to atomically exchange two paths. (Linux kernel 3.15, glibc 2.28, so it's been around for a while, but rename(1) and mv(1) don't have options to use it.)

If you're running a server, this might matter to you, otherwise you likely don't care.


Interactively, you might also use imv /mnt/data/mydata to interactively edit the path with readline line-editing (the same as bash uses for command-line editing.)

Score:5
jp flag

How it's done

The way it's done is you'd need to move the contents of:

/mnt/data/mydata/mydata/

to

/mnt/data/mydata/

first ... And afterwords you can just delete the now empty directory, if you like, with e.g.:

rmdir -v /mnt/data/mydata/mydata/

rmdir is safe ... It will only delete the directory if it's empty and error otherwise.

How to move

Either (with no dry-run) simply:

echo mv -nv -- /mnt/data/mydata/mydata/* /mnt/data/mydata/

Notice: echo before mv is just a safe guard from accidental copy/paste ... You need to remove it for the command to work ... No dry-run here, so be careful.

Or (with a dry-run) utilizing bash's shell parameter expansion around either of the repeated directory names (e.g. either the leading one with: "${parameter/mydata\//}" or the trailing one with:"${parameter%mydata/*}${parameter##*/}") and a for loop ... A one-liner would be something as simple as:

for f in /mnt/data/mydata/mydata/*; do echo mv -nv -- "${f}" "${f%mydata/*}${f##*/}"; done

Notice: echo is inserted before mv for a safe dry-run ... Remove echo only when satisfied with the output and run the command again to do the actual moving.

Demonstration:

$ tree /mnt
/mnt
└── data
    └── mydata
        └── mydata
            ├── dir1
            │   ├── file1
            │   ├── file2
            │   └── file3
            ├── dir2
            │   ├── file1
            │   ├── file2
            │   └── file3
            ├── dir3
            ├── file1
            ├── file2
            └── file3

6 directories, 9 files
$ for f in /mnt/data/mydata/mydata/*; do mv -nv -- "${f}" "${f%mydata/*}${f##*/}"; done
renamed '/mnt/data/mydata/mydata/dir1' -> '/mnt/data/mydata/dir1'
renamed '/mnt/data/mydata/mydata/dir2' -> '/mnt/data/mydata/dir2'
renamed '/mnt/data/mydata/mydata/dir3' -> '/mnt/data/mydata/dir3'
renamed '/mnt/data/mydata/mydata/file1' -> '/mnt/data/mydata/file1'
renamed '/mnt/data/mydata/mydata/file2' -> '/mnt/data/mydata/file2'
renamed '/mnt/data/mydata/mydata/file3' -> '/mnt/data/mydata/file3'
$ tree /mnt
/mnt
└── data
    └── mydata
        ├── dir1
        │   ├── file1
        │   ├── file2
        │   └── file3
        ├── dir2
        │   ├── file1
        │   ├── file2
        │   └── file3
        ├── dir3
        ├── file1
        ├── file2
        ├── file3
        └── mydata

6 directories, 9 files
I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.