简体   繁体   中英

move files that have the same name of the file they contain

Someone very clever (feel irony?) has extracted more than 6000 archives in their own directories, but sometimes the archive contain one file: the same name of the directory it contains.

Example:

mydir(0001)/mydir(0001).txt
mydir(ZREZ)/mydir(ZREZ).txt
mydir(AAEZ)/mydir(AAREZ).txt
mydir(AAEZ)/otherfile.txt
mydir(QQQQ)/mydir(QQQQ).txt
...

Is there a fast way (Unix shell) to compare the file (if there's only one file) and the directory where it's in, and if it's the same (without the extension), move it one directory above and remove the (now empty) directory?

So I should get:

mydir(0001).txt
mydir(ZREZ).txt
mydir(AAEZ)/mydir(AAREZ).txt
mydir(AAEZ)/otherfile.txt
mydir(QQQQ).txt
...

You can use a script like this from the parent directory of all those 6000 directories/files:

while IFS= read -d '' -r d; do
    n=$(find "$d" -maxdepth 1 -type f -exec echo . \; | wc -l)
    f="${d}/${d}.txt"
    [[ $n -eq 1 && -f "$f" ]] && mv "$f" "$d/../" && rmdir "$d"
done < <(find . -type d ! -name '.' -print0)

This should do the trick. A bit simpler than anubhava's script, but still careful about only moving files and removing dirs if that's the only content.

for x in *; do
  if [[ $(find "$x" | sed 1d | head -n2) == "$x/$x.txt" ]]; then
    mv "$x/$x.txt" . && rmdir "$x"
  fi
done

You can cut-n-paste this to the command line in the parent dir. It loops over all files in that dir ( for x in * ) and if any of them is itself a directory containing only the single file $x/$x.txt then it will move the file and remove the directory.

Here's my script, based on Anubhava's first answer. Sorry for the beginner's style:

#!/bin/bash
# loop & print a folder recusively,
folder_recurse() {
    for i in "$1"/*;do
        if [ -d "$i" ];then
            n=$(find "$i" -maxdepth 1 -exec echo . \; | wc -l)
            d=$(readlink -f "${i}")
            g=$d"/"$(basename "$i")".epub"
            if [[ $n -eq 2 && -f "$g" ]]; then
                echo "Doing: $i"
                mv "$g" "$d/.."
                rmdir "$d"
            fi
            folder_recurse "$i"
    done
}


# try get path from param
path=""
if [ -d "$1" ]; then
    path=$1;
else
    path="/tmp"
fi

echo "base path: $path"
folder_recurse $path

You can do it with a one-liner within the parent directory:

for i in */*; do [ -f "${i%/*}/${i%/*}.txt" ] && mv "$i" . && rm -d "${i%/*}"; done

Since there has been interest in a solution that function equally on BSD where there is no rm -d , the same can be accomplished with:

for i in */*; do [ -f "${i%/*}/${i%/*}.txt" ] && mv "$i" . && [ -z "$(ls -A "${i%/*}")" ] && rm -r "${i%/*}"; done

Which, following the mv check whether the remaining directory is empty, and if so, then removes the directory, otherwise leaving the directory unchanged.

output:

drwxr-xr-x 2 david david 4096 Aug 25 16:20 mydir(AAEZ)
-rw-r--r-- 1 david david    0 Aug 25 16:20 mydir(0001).txt
-rw-r--r-- 1 david david    0 Aug 25 16:20 mydir(QQQQ).txt
-rw-r--r-- 1 david david    0 Aug 25 16:20 mydir(ZREZ).txt

mydir\(AAEZ\)/
-rw-r--r-- 1 david david    0 Aug 25 16:20 mydir(AAREZ).txt
-rw-r--r-- 1 david david    0 Aug 25 16:20 otherfile.txt

If needed, you can also test that the directory is empty after moving the file -- before deleting the directory. Shown

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM