Unix: Delete all but N most recent files in a directory

Here’s a handy little command to delete every file in a directory except for the N most recent files. It is helpful for including in a log rotation or db backup script.

find /path/to/files/ -maxdepth 1 -type -f -name '*' -print0 | xargs -r0 ls -t | tail -n +5 | tr '\n' '\0' | xargs -r0 rm

Breaking down and explaining each section of the command, we have:

find /path/to/files/ -maxdepth 1 -type -f -name '*' -print0

This will list all files in the specified directory. The reason we use ‘find’ rather than ‘ls’ is because we need the full path of the files when later passing the argument list to the ‘rm’ command. We specify a ‘maxdepth’ so we only search within the current directory. We also specify a ‘type’ of ‘f’ so that we only find files and not other items like directories, sockets, or symbolic links. It’s probably best not to use ‘*’ as your ‘find’ expression, as it could be dangerous if you accidentally point it to the wrong directory. Use something like ‘*.sql.gz’ or ‘*.log’ or whatever suits you. Also note that we are using ‘-print0’ so that the subsequent commands will be able to handle spaces and other special characters in filenames (see http://en.wikipedia.org/wiki/Xargs#The_separator_problem)

xargs -r0 ls -t

This will sort the list returned by the ‘find’ command in descending order by timestamp (newest to oldest). The ‘-r’ parameter instructs xargs not to run of no files are found by the first ‘find’ command. The ‘-0’ parameter gets around the separator problem described in the previous step.

tail -n +5

This will filter the list to only show from the 5th line onward. So if you want to keep the 10 most recent files, use +10 as your argument.

tr '\n' '\0'

This is similar to using ‘-print0’ with the ‘find’ command above. We need to clean up the output so that the subsequent xargs command can handle potential spaces in the filenames. So we use the ‘tr’ command to translate newlines to the null character.

xargs -r0 rm

Finally, this command will delete the files returned by the combination of the previous commands.

How to increase bash shell history length in OS X

I do a lot of command line work on my OS X machine, so the history saves me a lot of time running repeat commands and also refreshing my memory on commands that I haven’t run in a while. Unfortunately, the default history length in OS X is 500 commands. That seems like a lot, but when you’re running 50+ commands a day it can push older commands off the list pretty quickly. This is easily solved by setting the HISTFILESIZE in your .bash_profile file.

First, to find out what HISTFILESIZE is currently set at, run the following command from Terminal:


To change this value, simply add the following line to your .bash_profile file (found in /Users/yourname/):


This increases your bash history to 2000 items. It will take effect next time you open a Terminal window.

phpList: processing with cron & bash script: “Command not found.”

If you are trying to process your phpList queue using a crontab, you will have to set up the phplist script provided in the bin directory to work with your system. It looks like this:
#!/bin/bash# script to run PHPlist from commandline. You may need to edit this to make it work
# with your shell environment. The following should work for Bash# in commandline mode, access is restricted to users who are listed in the config file
# check README.commandline for more info# identify the config file for your installation
export CONFIG# alternatively you can use -c on the commandline# run the PHPlist index file with all parameters passed to this script
/usr/bin/php /home/website/public_html/lists/admin/index.php $*

Of course you will want to modify all the paths to fit your phpList install. Most importantly, however, is to not forget to modify the FIRST line if need be. Otherwise, if your bash interpreter exists elsewhere, when your try to execute your script you will get the following error:
/path/to/phplist: Command not found.
For the majority of the installations out there, /bin/bash should be fine, however on some systems you may need to edit this to point to where your bash interpreter is installed. In my case, I had to change it to /usr/local/bin/bash.

To future-proof my changes, I also symlinked to where my bash existed:
ln -s /usr/local/bin/bash /bin/bash
That way I will not have to re-edit new versions of the phplist script in future phpList upgrades.