0

I have 100 joomla website in my server I want to clean all of joomla cache folders. I need a command to do this:

1- find all directories with "cache" name

if I search in for example "/home/demo/" directory

it find the "cache" directory in maximum one deeper level, like :

/home/demo/ddf3e/

/home/demo/awda2/

Not deeper than this

2- clean all files & directories inside of "cache"s directories

Only the contents to be remove not itself

3- creat an "index.html" file in all "cache"s directories

Thank you in advance

2 Answers 2

2

The most POSIX way to do this is probably to use the find command with multiple -exec options.

Update: A fully POSIX solution is at the end of this post.

You can locate nested directories named "cache" under the /home/demo path like so:

find /home/demo -mindepth 2 -maxdepth 2 -type d -name cache
  • mindepth and maxdepth values of 2 restrict the search to immediate child directories
  • -type d designates that you only want to find directories
  • -name cache tells it to only return matches that are named exactly "cache"

You can then use the -exec option to perform actions based on matched results. For example, if you wanted to run ls to list the contents of each matched cache directory:

find /home/demo -mindepth 2 -maxdepth 2 -type d -name cache -exec ls {} \;
  • -exec calls the following command that follows it (ls) for each match
  • {} returns the value of the match (in this case the path to the cache directory)
  • \; tells it to perform the command one time for each match

So if you want to delete the contents of the directory, you can replace the ls command above with an appropriate rm statement—something like sh -c 'rm "{}"/*'

Example (DO NOT RUN THIS):

find /home/demo -maxdepth 2 -mindepth 2 -type d -name cache -exec sh -c 'rm "{}"/* 2>/dev/null' \;

In the above example sh -c is used to allow for shell expansion of the /* to match nested files (that's not very POSIX though). You could then add another -exec as well to create an additional file.

Example (DO NOT RUN THIS):

 find /home/demo -maxdepth 2 -mindepth 2 -type d -name cache -exec sh -c 'rm "{}"/* 2>/dev/null' \; -exec touch "{}/index.html" \;

In the above example two -exec options are used together. The second one, which creates a nested index.html file, only runs if the first -exec command completes successfully.

It's maybe better to split this into multiple find commands, though—one to match and remove the files, and another to create the index.html.

Example (DO NOT RUN THIS):

find /home/demo -mindepth 3 -maxdepth 3 -wholename "/home/demo/*/cache/*" -exec rm -rf "{}" \;
find /home/demo -maxdepth 2 -mindepth 2 -type d -name cache -exec touch "{}/index.html" \;

In the first statement:

  • -mindepth and -maxdepth of 3 only matches children of children
  • -wholename "/home/demo/*/cache/*" provides a match to children of the target cache directories

The second statement then creates the index.html files.

If I understood your question correctly, then that command pair probably does what you're looking for. But read it carefully and make sure you fully understand it before using it.

I recommend prefixing any rm and touch commands with the echo command before running anything. That should let you have a dry run to see what it's doing.

Example (safe dry run using echo):

find /home/demo -mindepth 3 -maxdepth 3 -wholename "/home/demo/*/cache/*" -exec echo rm -rf "{}" \;
find /home/demo -maxdepth 2 -mindepth 2 -type d -name cache -exec echo touch "{}/index.html" \;

Update - Fully POSIX Solution (but harder to read):

For a fully POSIX equivalent (no mindepth/maxdepth/wholename) you can use a positive/negative -path combination using find's internal globbing.

It's significantly easier if you cd into your working tree first, as you won't have to count the leading directories for your glob patterns.

cd /home/demo
find . -path "./*/cache/*" ! -path "./*/*/*/*" -exec echo rm -rf {} \;
find . -type d -path "./*/cache" ! -path "./*/*/*" -exec echo touch {}/index.html \;

Remove the echo statements if you get the expected result.

4
  • I completely forgot about the -delete option. ilkkachu's answer is a far better choice for content removal. But I would recommend against using find with shell expansion (e.g. find */cache) in a script unless it's a one-off or you know your target shell settings. Sep 11, 2016 at 20:17
  • 1
    -delete and -min/maxdepth aren't found in all versions of find, which is why I specifically mentioned GNU find. Good point on the glob, better use ./* here, too.
    – ilkkachu
    Sep 12, 2016 at 11:39
  • @ilkkachu good point on -min/maxdepth. I've updated the post with what I believe to be a fully POSIX solution. Preferable to avoid any specific version options where we can. Sep 12, 2016 at 15:21
  • This was a great education. You gave me a lot of help and I owe you.
    – HamidZ
    Jul 25, 2017 at 10:58
2

So, if I got it right, you want to find e.g. /home/demo/foo/cache for all values of foo. Since you don't need to look in deeper levels, it's rather simple.

(1) You can list them with a shell glob:

$ cd /home/demo
$ echo */cache

To guard against funny names like those starting with a dash (-foo), we'd better use ./*. Also, in bash, you can also make the glob include names starting with a dot by using shopt -s dotglob.

(2) Find and delete the files inside the directories (with GNU find, -mindepth 1 keeps it from matching the directories themselves):

$ shopt -s dotglob
$ find ./*/cache -mindepth 1                          # list them
./awda2/cache/foo
[...]
$ find ./*/cache -mindepth 1 -delete                  # delete them...

You could also use just rm, but with a huge amount of files you might run past the limit for command line length.

$ rm -r ./*/cache/*

(3) Create or copy a file to all of them (touch creates an empty file):

$ for dir in ./*/cache ; do touch "$dir/index.html" ; done
$ for dir in ./*/cache ; do cp index.html.model  "$dir/index.html" ; done

(If there's a file (not directory) named cache, you'll get a harmless error about that.)

2
  • You started with globs, why not finish with them? rm -rf ./*/cache/*
    – Centimane
    Sep 12, 2016 at 11:42
  • @Centimane, Mostly because I originally ignored dotglob for simplicity, so any dotfiles in the cache directories would be left there. Another reason is that the command line length may be limited, and with a hundreds of dirs and files you might hit that. Depends on the system though, the two Linux systems I checked had the limit at 128 kB and 2 MB. The latter should be enough for a while. Also find came first to mind. ;) But I'll admit, that would remove the requirement for -mindepth
    – ilkkachu
    Sep 12, 2016 at 12:04

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .