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.