3

I like to loop though subdirectories and move the biggest file to its parent and then delete the folder. I have tried many things but never got it working correctly.

It says that mv cant find the file or folder on this one.

dir[123]/something.mkv
dir[123]/something-small.txt
dir[555]/otherthing.mkv
dir[555]/otherthing-small.txt

Script:

#!/bin/bash

for d in */ ; do
    FIND=$(find . -type f -exec ls -al {} \; | sort -nr -k5 | head -n 1)
    echo "$d${FIND##*/}"
    mv "$d${FIND##*/}" .
done
  • Hmmm... why not use ls -1 --sort=size dir| head -1 for finding the biggest file in a dir? Then it's just a matter of looping through them... but be careful, size and length of a file are not always the same thing. What are you trying to do exactly? – Rmano May 5 '15 at 14:44
  • 1
    @Rmano because that will break if the file names contain spaces, tabs, newlines, shell glob characters or backslashes. find is a much better approach. – terdon May 5 '15 at 14:44
  • @terdon They're using ls into the loop too --- doubt it will work with spaces. "$(ls -1 --sort=size dir| head -1)" will work with spaces and tabs and similar, and if they have newlines in filenames, then they deserve it :-P – Rmano May 5 '15 at 14:47
  • @Rmano (scratch the last version if you saw it), yes, you're quite right. – terdon May 5 '15 at 14:51
4

It is failing because you are searching in the current directory and not the target. Change your find to:

find "$d" -type f -exec ls -al {} \; | sort -nr -k5 | head -n 1

However, a safer way would be:

#!/bin/bash
for d in */ ; do
    file=$(find "$d" -type f -printf "%s\t%f\n" | sort -n | tail -n 1 | cut -f 2- )
    echo "$file"
    mv "$d${file}" .
done

While this will still break if your file names contain newlines, at least it will work with file names containing spaces.


If your files can also contain newlines, you can try this instead:

#!/bin/bash
for d in */ ; do
    max=0
    file=""
    for f in "$d"/*
    do
        ## If this is a file and its size is greater than the max so far,
        ## save it as "$file" and uts size as "$max".
        [[ -f "$f" ]] && [[ $(stat -c "%s" "$f") -gt $max ]] && max="$size" && file="$f"
    done
    ## move the largest file. Use "mv -i" to be prompted before overwriting. 
    mv "$file" .
done
  • Newlines in filenames, do you mean if the find output would break into a new line or can we have actual newline characters in filenames? – redanimalwar May 5 '15 at 16:40
  • @redanimalwar the latter. Try touch foo$'\n'bar. The only characters not allowed in file names are / and \0. – terdon May 5 '15 at 16:43
  • Thanks, did not know this but I do not care, I see no value in having newlines in filenames and never came across any file that had this, well at least not that I know of. – redanimalwar May 5 '15 at 17:45
  • @redanimalwar of course not, I doubt anyone would create one on purpose. On the other hand, you should never underestimate the ability of random users to screw up a system and it is good practice to always make your scripts robust and capable of dealing with all possible file names. – terdon May 6 '15 at 10:44
2

Use this

find "$d" -type f ...

instead of

find . -type f ...

. is your current folder and you mean the elements provided by $d.

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.