1

I'm trying to find all files in a file structure above a certain file size, list them, then delete them. What I currently have looks like this:

filesToDelete=$(find $find $1 -type f -size +$2k -ls)
if [ -n "$filesToDelete" ];then
echo "Deleting files..."
echo $filesToDelete
$filesToDelete | xargs rm
else 
echo "no files to delete"
fi

Everything works, except the $filesToDelete | xargs rm, obviously. Is there a way to use pipe on a variable? Or is there another way I could do this? My google-fu didn't really find anything, so any help would be appreciated.

Edit: Thanks for the information everyone. I will post the working code here now for anyone else stumbling upon this question later:

if [ $(find $1 -type f -size +$2k | wc -l) -ge 1 ]; then
find $1 -type f -size +$2k -exec sh -c 'f={}; echo "deleting file $f"; rm $f' {} \;
else
echo "no files above" $2 "kb found"
fi
2
  • 2
    Why don't you handle everything from the find command? find ... -exec rm {} \; Commented Sep 12, 2014 at 10:07
  • @fedorqui I might add that I'm a complete newbie when it comes to Bash and anything Linux related. I tried adding -exec rm {} \ at the end of the find, and that just throws errors in my face (unexpected EOF, syntax error, etc.). That said, if I delete them as part of setting filesToDelete, will I be able to print the list afterwards (considering they've been, well, deleted)? Commented Sep 12, 2014 at 10:13

3 Answers 3

4

As already pointed out, you don't need piping a var in this case. But just in case you needed it in some other situation, you can use

xargs rm <<< $filesToDelete

or, more portably

echo $filesToDelete | xargs rm

Beware of spaces in file names.

To also output the value together with piping it, use tee with process substitution:

echo "$x" | tee >( xargs rm )
Sign up to request clarification or add additional context in comments.

6 Comments

I tried your second example, and it works (if I remove -ls from the find). The problem is... when I do that, it doesn't actually print/echo the removed files. I can do echo two times (one with and without xargs rm), but it seems kinda redundant... Well, at least it works.
tee looks interesting. Can you use tee to run | xargs rm in the find statement, but keep the list of files as the variable?
I'd give you an upvote, but I don't have the rep to do that on this account. Regardless, thanks for the information!
+1 because you're the only who answered the OP's question - how to pipe from a variable ;) :)
@user2875994: I don't understand. You mean you want to use the find | xargs syntax, but sneak the tee in to populate a variable? It's not possible, as the >(...) part happens in a subshell, so any changes to variables there are inaccessible for the parent. Maybe you could switch to printf -v, but still, you are in a middle of a pipeline, i.e. in a subshell.
|
1

You can directly use -exec to perform an action on the files that were found in find:

find $1 -type f -size +$2k -exec rm {} \;

The -exec trick makes find execute the command given for each one of the matches found. To refer the match itself we have to use {} \;.

If you want to perform more than one action, -exec sh -c "..." makes it. For example, here you can both print the name of the files are about to be removed... and remove them. Note the f={} thingy to store the name of the file, so that it can be used later on in echo and rm:

find $1 -type f -size +$2k -exec sh -c 'f={}; echo "removing $f"; rm $f' {} \;

In case you want to print a message if no matches were found, you can use wc -l to count the number of matches (if any) and do an if / else condition with it:

if [ $(find $1 -type f -size +$2k | wc -l) -ge 1 ]; then
   find $1 -type f -size +$2k -exec rm {} \;
else
   echo "no matches found"
fi

wc is a command that does word count (see man wc for more info). Doing wc -l counts the number of lines. So command | wc -l counts the number of lines returned by command.

Then we use the if [ $(command | wc -l) -ge 1 ] check, which does an integer comparison: if the value is greater or equal to 1, then do what follows; otherwise, do what is in else.


Buuuut the previous approach was using find twice, which is a bit inefficient. As -exec sh -c is opening a sub-shell, we cannot rely on a variable to keep track of the number of files opened. Why? Because a sub-shell cannot assign values to its parent shell.

Instead, let's store the files that were deleted into a file, and then count it:

find . -name "*.txt" -exec sh -c 'f={}; echo "$f" >> /tmp/findtest; rm $f' {} \;
if [ -s /tmp/findtest ]; then #check if the file is empty
    echo "file has $(wc -l < /tmp/findtest) lines"
    # you can also `cat /tmp/findtest` here to show the deleted files
else
    echo "no matches"
fi

Note that you can cat /tmp/findtest to see the deleted files, or also use echo "$f" alone (without redirection) to indicate while removing. rm /tmp/findtest is also an option, to do once the process is finished.

10 Comments

What does 'f={];' do? Except for that I think I kind of understand it
{} is the way to refer to the name of the file that was found. So with f={} we store it in a variable, so that we can use it to both echo and rm.
Well, that ended up working as far as deleting goes, but it didn't echo anything.
@user2875994 strange. This works fine to me: ` find . -name "*.txt" -exec sh -c 'f={}; echo "hello $f"; rm $f' {} \;` . You can also use the first approach twice: firstly without -exec rm {} \; to get the list of files; then, with it, to remove them.
Nevermind, worked as soon as I didn't set it as a variable (why would that make it not echo, though?). That said, I need to give a message when it doesn't find any files, which doesn't seem to work with this solution.
|
1

You don't need to do all this. You can directly use find command to get the files over a particular size limit and delete it using xargs.

This should work:

#!/bin/bash
if [ $(find $1 -type f -size +$2k | wc -l) -eq 0 ]; then
    echo "No Files to delete"
else
    echo "Deleting the following files"
    find $1 -size +$2 -exec ls {} \+
    find $1 -size +$2 -exec ls {} \+ | xargs rm -f
    echo "Done"
fi

3 Comments

I realize that, but I want to print/echo the list as well (if it exists). That's where the hiccups start.
I do understand your requirement that is why I used /usr/bin/find <directory path> -size +<size limit> -exec ls -lh {} \+ in my answer. Use this command to list the files. For example, /usr/bin/find $1 -size +$2k -exec ls -lh {} \+
Seems like your edit is very close to what fedorqui explained to me (edited into OP), although it ha a bit more redundancy. Regardless, thanks for helping. Sadly I cannot upvote due to the account being new.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.