user2875994
user2875994

Reputation: 305

Is there a way to pipe from a variable?

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

Upvotes: 1

Views: 196

Answers (3)

fedorqui
fedorqui

Reputation: 289495

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.

Upvotes: 1

iqstatic
iqstatic

Reputation: 2382

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

Upvotes: 1

choroba
choroba

Reputation: 241748

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 )

Upvotes: 4

Related Questions