<p>Is there a simple way, in a pretty standard UNIX environment with bash, to run a command to delete all but the most recent X files from a directory?</p>

<p>To give a bit more of a concrete example, imagine some cron job writing out a file (say, a log file or a tar-ed up backup) to a directory every hour. I'd like a way to have another cron job running which would remove the oldest files in that directory until there are less than, say, 5.</p>

<p>And just to be clear, there's only one file present, it should never be deleted.</p>

[{'answer_id': 25789, 'author': 'Espo', 'author_id': 2257, 'author_profile': 'https://Stackoverflow.com/users/2257', 'pm_score': 7, 'selected': False, 'text': "<p>Remove all but 5 (or whatever number) of the most recent files in a directory.</p>\n\n<pre><code>rm `ls -t | awk 'NR&gt;5'`\n</code></pre>\n"}, {'answer_id': 25790, 'author': 'thelsdj', 'author_id': 163, 'author_profile': 'https://Stackoverflow.com/users/163', 'pm_score': 6, 'selected': False, 'text': '<pre><code>(ls -t|head -n 5;ls)|sort|uniq -u|xargs rm\n</code></pre>\n\n<p>This version supports names with spaces:</p>\n\n<pre><code>(ls -t|head -n 5;ls)|sort|uniq -u|sed -e \'s,.*,"&amp;",g\'|xargs rm\n</code></pre>\n'}, {'answer_id': 25792, 'author': 'Mark Harrison', 'author_id': 116, 'author_profile': 'https://Stackoverflow.com/users/116', 'pm_score': 2, 'selected': False, 'text': '<p>If the filenames don\'t have spaces, this will work:</p>\n\n<pre><code>ls -C1 -t| awk \'NR&gt;5\'|xargs rm\n</code></pre>\n\n<p>If the filenames do have spaces, something like</p>\n\n<pre><code>ls -C1 -t | awk \'NR&gt;5\' | sed -e "s/^/rm \'/" -e "s/$/\'/" | sh\n</code></pre>\n\n<p>Basic logic:</p>\n\n<ul>\n<li>get a listing of the files in time order, one column</li>\n<li>get all but the first 5 (n=5 for this example)</li>\n<li>first version: send those to rm</li>\n<li>second version: gen a script that will remove them properly</li>\n</ul>\n'}, {'answer_id': 299534, 'author': 'Community', 'author_id': -1, 'author_profile': 'https://Stackoverflow.com/users/-1', 'pm_score': 4, 'selected': False, 'text': "<p>All these answers fail when there are directories in the current directory. Here's something that works:</p>\n\n<pre><code>find . -maxdepth 1 -type f | xargs -x ls -t | awk 'NR&gt;5' | xargs -L1 rm\n</code></pre>\n\n<p>This:</p>\n\n<ol>\n<li><p>works when there are directories in the current directory</p></li>\n<li><p>tries to remove each file even if the previous one couldn't be removed (due to permissions, etc.)</p></li>\n<li><p>fails safe when the number of files in the current directory is excessive and <code>xargs</code> would normally screw you over (the <code>-x</code>)</p></li>\n<li><p>doesn't cater for spaces in filenames (perhaps you're using the wrong OS?)</p></li>\n</ol>\n"}, {'answer_id': 299911, 'author': 'wnoise', 'author_id': 15464, 'author_profile': 'https://Stackoverflow.com/users/15464', 'pm_score': 4, 'selected': False, 'text': '<pre><code>find . -maxdepth 1 -type f -printf \'%T@ %p\\0\' | sort -r -z -n | awk \'BEGIN { RS="\\0"; ORS="\\0"; FS="" } NR &gt; 5 { sub("^[0-9]*(.[0-9]*)? ", ""); print }\' | xargs -0 rm -f\n</code></pre>\n\n<p>Requires GNU find for -printf, and GNU sort for -z, and GNU awk for "\\0", and GNU xargs for -0, but handles files with embedded newlines or spaces.</p>\n'}, {'answer_id': 990575, 'author': 'Ian Kelling', 'author_id': 14456, 'author_profile': 'https://Stackoverflow.com/users/14456', 'pm_score': 3, 'selected': False, 'text': '<p>Ignoring newlines is ignoring security and good coding. wnoise had the only good answer. Here is a variation on his that puts the filenames in an array $x</p>\n\n<pre><code>while IFS= read -rd \'\'; do \n    x+=("${REPLY#* }"); \ndone &lt; &lt;(find . -maxdepth 1 -printf \'%T@ %p\\0\' | sort -r -z -n )\n</code></pre>\n'}, {'answer_id': 8216367, 'author': 'lolesque', 'author_id': 787216, 'author_profile': 'https://Stackoverflow.com/users/787216', 'pm_score': 2, 'selected': False, 'text': "<p>With zsh</p>\n\n<p>Assuming you don't care about present directories and you will not have more than 999 files (choose a bigger number if you want, or create a while loop).</p>\n\n<pre><code>[ 6 -le `ls *(.)|wc -l` ] &amp;&amp; rm *(.om[6,999])\n</code></pre>\n\n<p>In <code>*(.om[6,999])</code>, the <code>.</code> means files, the <code>o</code> means sort order up, the <code>m</code> means by date of modification (put <code>a</code> for access time or <code>c</code> for inode change), the <code>[6,999]</code> chooses a range of file, so doesn't rm the 5 first.</p>\n"}, {'answer_id': 10119963, 'author': 'Fabien', 'author_id': 21132, 'author_profile': 'https://Stackoverflow.com/users/21132', 'pm_score': 6, 'selected': False, 'text': "<p>Simpler variant of thelsdj's answer:</p>\n\n<pre><code>ls -tr | head -n -5 | xargs --no-run-if-empty rm \n</code></pre>\n\n<p>ls -tr displays all the files, oldest first (-t newest first, -r reverse).</p>\n\n<p>head -n -5 displays all but the 5 last lines (ie the 5 newest files).</p>\n\n<p>xargs rm calls rm for each selected file.</p>\n"}, {'answer_id': 16978637, 'author': 'Pavel Tankov', 'author_id': 1154183, 'author_profile': 'https://Stackoverflow.com/users/1154183', 'pm_score': 0, 'selected': False, 'text': '<pre><code>leaveCount=5\nfileCount=$(ls -1 *.log | wc -l)\ntailCount=$((fileCount - leaveCount))\n\n# avoid negative tail argument\n[[ $tailCount &lt; 0 ]] &amp;&amp; tailCount=0\n\nls -t *.log | tail -$tailCount | xargs rm -f\n</code></pre>\n'}, {'answer_id': 17850033, 'author': 'Mark', 'author_id': 3915, 'author_profile': 'https://Stackoverflow.com/users/3915', 'pm_score': 4, 'selected': False, 'text': '<pre><code>ls -tQ | tail -n+4 | xargs rm\n</code></pre>\n\n<p>List filenames by modification time, quoting each filename. Exclude first 3 (3 most recent). Remove remaining.</p>\n\n<p>EDIT after helpful comment from mklement0 (thanks!): corrected -n+3 argument, and note this will not work as expected if filenames contain newlines and/or the directory contains subdirectories.</p>\n'}, {'answer_id': 34862475, 'author': 'mklement0', 'author_id': 45375, 'author_profile': 'https://Stackoverflow.com/users/45375', 'pm_score': 8, 'selected': False, 'text': '<p>The problems with the existing answers:</p>\n<ul>\n<li>inability to handle filenames with embedded spaces or newlines.\n<ul>\n<li>in the case of solutions that invoke <code>rm</code> directly on an unquoted command substitution (<code>rm `...`</code>), there\'s an added risk of unintended globbing.</li>\n</ul>\n</li>\n<li>inability to distinguish between files and directories (i.e., if <em>directories</em> happened to be among the 5 most recently modified filesystem items, you\'d effectively retain <em>fewer</em> than 5 files, and applying <code>rm</code> to directories will fail).</li>\n</ul>\n<p><a href="https://stackoverflow.com/a/299911/45375">wnoise\'s answer</a> addresses these issues, but the solution is <em>GNU</em>-specific (and quite complex).</p>\n<p>Here\'s a pragmatic, <strong>POSIX-compliant solution</strong> that comes with only <strong>one caveat</strong>: it cannot handle filenames with embedded <em>newlines</em> - but I don\'t consider that a real-world concern for most people.</p>\n<p><sup>For the record, here\'s the explanation for why it\'s generally not a good idea to parse <code>ls</code> output: <a href="http://mywiki.wooledge.org/ParsingLs" rel="noreferrer">http://mywiki.wooledge.org/ParsingLs</a></sup></p>\n<pre><code>ls -tp | grep -v \'/$\' | tail -n +6 | xargs -I {} rm -- {}\n</code></pre>\n<p><sup>Note: This command operates in the <strong><em>current</em> directory</strong>; to <strong>target a directory <em>explicitly</em>, use a subshell (<code>(...)</code>) with <code>cd</code></strong>:<br />\n<code>(cd /path/to &amp;&amp; ls -tp | grep -v \'/$\' | tail -n +6 | xargs -I {} rm -- {})</code><br />\nThe same <strong>applies analogously to the commands below</strong>.</sup></p>\n<p>The above is <strong>inefficient</strong>, because <code>xargs</code> has to invoke <code>rm</code> separately <em>for each filename</em>.<br />\nHowever, your platform\'s specific <code>xargs</code> implementation may allow you to solve this problem:</p>\n<hr />\n<p>A solution that <strong>works with <em>GNU</em> <code>xargs</code></strong> is to use <strong><code>-d \'\\n\'</code></strong>, which makes <code>xargs</code> consider each input line a separate argument, yet passes as many arguments as will fit on a command line <em>at once</em>:</p>\n<pre><code>ls -tp | grep -v \'/$\' | tail -n +6 | xargs -d \'\\n\' -r rm --\n</code></pre>\n<p><sup>Note: Option <code>-r</code> (<code>--no-run-if-empty</code>) ensures that <code>rm</code> is not invoked if there\'s <em>no input</em>.</sup></p>\n<p>A solution that <strong>works with <em>both</em> <em>GNU</em> <code>xargs</code> <em>and</em> <em>BSD</em> <code>xargs</code></strong> (including on <strong>macOS</strong>) - though technically still <em>not</em> POSIX-compliant - is to use <strong><code>-0</code></strong> to handle <code>NUL</code>-separated input, after first translating newlines to <code>NUL</code> (<code>0x0</code>) chars., which also passes (typically) all filenames <em>at once</em>:</p>\n<pre><code>ls -tp | grep -v \'/$\' | tail -n +6 | tr \'\\n\' \'\\0\' | xargs -0 rm --\n</code></pre>\n<p><strong>Explanation:</strong></p>\n<ul>\n<li><p><code>ls -tp</code> prints the names of filesystem items sorted by how recently they were modified , in descending order (most recently modified items first) (<code>-t</code>), with directories printed with a trailing <code>/</code> to mark them as such (<code>-p</code>).</p>\n<ul>\n<li>Note: It is the fact that <code>ls -tp</code> always outputs file / directory <em>names</em> only, not full paths, that necessitates the subshell approach mentioned above for targeting a directory other than the current one (<code>(cd /path/to &amp;&amp; ls -tp ...)</code>).</li>\n</ul>\n</li>\n<li><p><code>grep -v \'/$\'</code> then weeds out directories from the resulting listing, by omitting (<code>-v</code>) lines that have a trailing <code>/</code> (<code>/$</code>).</p>\n<ul>\n<li><em>Caveat</em>: Since a <em>symlink that points to a directory</em> is technically not itself a directory, such symlinks will <em>not</em> be excluded.</li>\n</ul>\n</li>\n<li><p><code>tail -n +6</code> skips the first <em>5</em> entries in the listing, in effect returning all <em>but</em> the 5 most recently modified files, if any.<br />\nNote that in order to exclude <code>N</code> files, <code>N+1</code> must be passed to <code>tail -n +</code>.</p>\n</li>\n<li><p><code>xargs -I {} rm -- {}</code> (and its variations) then invokes on <code>rm</code> on all these files; if there are no matches at all, <code>xargs</code> won\'t do anything.</p>\n<ul>\n<li><code>xargs -I {} rm -- {}</code> defines placeholder <code>{}</code> that represents each input line <em>as a whole</em>, so <code>rm</code> is then invoked once for each input line, but with filenames with embedded spaces handled correctly.</li>\n<li><code>--</code> in all cases ensures that any filenames that happen to start with <code>-</code> aren\'t mistaken for <em>options</em> by <code>rm</code>.</li>\n</ul>\n</li>\n</ul>\n<hr />\n<p>A <strong>variation</strong> on the original problem, <strong>in case the matching files need to be processed <em>individually</em> or <em>collected in a shell array</em></strong>:</p>\n<pre><code># One by one, in a shell loop (POSIX-compliant):\nls -tp | grep -v \'/$\' | tail -n +6 | while IFS= read -r f; do echo &quot;$f&quot;; done\n\n# One by one, but using a Bash process substitution (&lt;(...), \n# so that the variables inside the `while` loop remain in scope:\nwhile IFS= read -r f; do echo &quot;$f&quot;; done &lt; &lt;(ls -tp | grep -v \'/$\' | tail -n +6)\n\n# Collecting the matches in a Bash *array*:\nIFS=$\'\\n\' read -d \'\' -ra files  &lt; &lt;(ls -tp | grep -v \'/$\' | tail -n +6)\nprintf \'%s\\n\' &quot;${files[@]}&quot; # print array elements\n</code></pre>\n'}, {'answer_id': 35184103, 'author': 'Bulrush', 'author_id': 3617046, 'author_profile': 'https://Stackoverflow.com/users/3617046', 'pm_score': 0, 'selected': False, 'text': '<p>I made this into a bash shell script. Usage: <code>keep NUM DIR</code> where NUM is the number of files to keep and DIR is the directory to scrub.</p>\n\n<pre><code>#!/bin/bash\n# Keep last N files by date.\n# Usage: keep NUMBER DIRECTORY\necho ""\nif [ $# -lt 2 ]; then\n    echo "Usage: $0 NUMFILES DIR"\n    echo "Keep last N newest files."\n    exit 1\nfi\nif [ ! -e $2 ]; then\n    echo "ERROR: directory \'$1\' does not exist"\n    exit 1\nfi\nif [ ! -d $2 ]; then\n    echo "ERROR: \'$1\' is not a directory"\n    exit 1\nfi\npushd $2 &gt; /dev/null\nls -tp | grep -v \'/\' | tail -n +"$1" | xargs -I {} rm -- {}\npopd &gt; /dev/null\necho "Done. Kept $1 most recent files in $2."\nls $2|wc -l\n</code></pre>\n'}, {'answer_id': 39280903, 'author': 'tim', 'author_id': 6785145, 'author_profile': 'https://Stackoverflow.com/users/6785145', 'pm_score': 1, 'selected': False, 'text': '<p>found interesting cmd in Sed-Onliners - Delete last 3 lines - fnd it perfect for another way to skin the cat (okay not) but idea:</p>\n\n<pre><code> #!/bin/bash\n # sed cmd chng #2 to value file wish to retain\n\n cd /opt/depot \n\n ls -1 MyMintFiles*.zip &gt; BigList\n sed -n -e :a -e \'1,2!{P;N;D;};N;ba\' BigList &gt; DeList\n\n for i in `cat DeList` \n do \n echo "Deleted $i" \n rm -f $i  \n #echo "File(s) gonzo " \n #read junk \n done \n exit 0\n</code></pre>\n'}, {'answer_id': 41579911, 'author': 'TopherGopher', 'author_id': 679458, 'author_profile': 'https://Stackoverflow.com/users/679458', 'pm_score': 3, 'selected': False, 'text': '<p>I realize this is an old thread, but maybe someone will benefit from this. This command will find files in the current directory :</p>\n\n<pre><code>for F in $(find . -maxdepth 1 -type f -name "*_srv_logs_*.tar.gz" -printf \'%T@ %p\\n\' | sort -r -z -n | tail -n+5 | awk \'{ print $2; }\'); do rm $F; done\n</code></pre>\n\n<p>This is a little more robust than some of the previous answers as it allows to limit your search domain to files matching expressions. First, find files matching whatever conditions you want. Print those files with the timestamps next to them.</p>\n\n<pre><code>find . -maxdepth 1 -type f -name "*_srv_logs_*.tar.gz" -printf \'%T@ %p\\n\'\n</code></pre>\n\n<p>Next, sort them by the timestamps:</p>\n\n<pre><code>sort -r -z -n\n</code></pre>\n\n<p>Then, knock off the 4 most recent files from the list:</p>\n\n<pre><code>tail -n+5\n</code></pre>\n\n<p>Grab the 2nd column (the filename, not the timestamp):</p>\n\n<pre><code>awk \'{ print $2; }\'\n</code></pre>\n\n<p>And then wrap that whole thing up into a for statement:</p>\n\n<pre><code>for F in $(); do rm $F; done\n</code></pre>\n\n<p>This may be a more verbose command, but I had much better luck being able to target conditional files and execute more complex commands against them. </p>\n'}, {'answer_id': 44247584, 'author': 'fabrice', 'author_id': 8082652, 'author_profile': 'https://Stackoverflow.com/users/8082652', 'pm_score': 1, 'selected': False, 'text': '<p>Removes all but the 10 latest (most recents) files</p>\n\n<pre><code>ls -t1 | head -n $(echo $(ls -1 | wc -l) - 10 | bc) | xargs rm\n</code></pre>\n\n<p>If less than 10 files no file is removed and you will have :\nerror head: illegal line count -- 0</p>\n\n<p><a href="http://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x700.html" rel="nofollow noreferrer">To count files with bash</a></p>\n'}, {'answer_id': 52632917, 'author': 'Pila', 'author_id': 9901844, 'author_profile': 'https://Stackoverflow.com/users/9901844', 'pm_score': 1, 'selected': False, 'text': '<p>I needed an elegant solution for the busybox (router), all xargs or array solutions were useless to me - no such command available there. find and mtime is not the proper answer as we are talking about 10 items and not necessarily 10 days. Espo\'s answer was the shortest and cleanest and likely the most unversal one.</p>\n\n<p>Error with spaces and when no files are to be deleted are both simply solved the standard way:</p>\n\n<pre><code>rm "$(ls -td *.tar | awk \'NR&gt;7\')" 2&gt;&amp;-\n</code></pre>\n\n<p>Bit more educational version: We can do it all if we use awk differently. Normally, I use this method to pass (return) variables from the awk to the sh. As we read all the time that can not be done, I beg to differ: here is the method.</p>\n\n<p>Example for .tar files with no problem regarding the spaces in the filename. To test, replace "rm" with the "ls".</p>\n\n<pre><code>eval $(ls -td *.tar | awk \'NR&gt;7 { print "rm \\"" $0 "\\""}\')\n</code></pre>\n\n<p>Explanation:</p>\n\n<p><code>ls -td *.tar</code> lists all .tar files sorted by the time. To apply to all the files in the current folder, remove the "d *.tar" part</p>\n\n<p><code>awk \'NR&gt;7...</code> skips the first 7 lines </p>\n\n<p><code>print "rm \\"" $0 "\\""</code> constructs a line: rm "file name"</p>\n\n<p><code>eval</code> executes it</p>\n\n<p>Since we are using <code>rm</code>, I would not use the above command in a script! Wiser usage is:</p>\n\n<pre><code>(cd /FolderToDeleteWithin &amp;&amp; eval $(ls -td *.tar | awk \'NR&gt;7 { print "rm \\"" $0 "\\""}\'))\n</code></pre>\n\n<p>In the case of using <code>ls -t</code> command will not do any harm on such silly examples as: <code>touch \'foo " bar\'</code> and <code>touch \'hello * world\'</code>. Not that we ever create files with such names in real life!</p>\n\n<p>Sidenote. If we wanted to pass a variable to the sh this way, we would simply modify the print (simple form, no spaces tolerated):</p>\n\n<pre><code>print "VarName="$1\n</code></pre>\n\n<p>to set the variable <code>VarName</code> to the value of <code>$1</code>. Multiple variables can be created in one go. This <code>VarName</code> becomes a normal sh variable and can be normally used in a script or shell afterwards. So, to create variables with awk and give them back to the shell:</p>\n\n<pre><code>eval $(ls -td *.tar | awk \'NR&gt;7 { print "VarName=\\""$1"\\""  }\'); echo "$VarName"\n</code></pre>\n'}, {'answer_id': 73465873, 'author': 'Eduardo Lucio', 'author_id': 3223785, 'author_profile': 'https://Stackoverflow.com/users/3223785', 'pm_score': 2, 'selected': False, 'text': "<p>Adaptation of @mklement0's excellent answer with some parameters and without needing to navigate to the folder containing the files to be deleted...</p>\n<pre><code>TARGET_FOLDER=&quot;/my/folder/path&quot;\nFILES_KEEP=5\nls -tp &quot;$TARGET_FOLDER&quot;**/* | grep -v '/$' | tail -n +$((FILES_KEEP+1)) | xargs -d '\\n' -r rm --\n</code></pre>\n<p>[Ref(s).: https://stackoverflow.com/a/3572628/3223785 ]</p>\n<p><strong>Thanks!</strong> </p>\n"}, {'answer_id': 73613596, 'author': 'Fravadona', 'author_id': 3387716, 'author_profile': 'https://Stackoverflow.com/users/3387716', 'pm_score': 4, 'selected': True, 'text': '<p><strong>For Linux</strong> (GNU tools), an efficient &amp; robust way to keep the <code>n</code> newest files in the current directory while removing the rest:</p>\n<pre class="lang-bash prettyprint-override"><code>n=5\n\nfind . -maxdepth 1 -type f -printf \'%T@ %p\\0\' |\nsort -z -nrt \' \' -k1,1 |\nsed -z -e &quot;1,${n}d&quot; -e \'s/[^ ]* //\' |\nxargs -0r rm -f\n</code></pre>\n<hr />\n<p><strong>For BSD</strong>, <code>find</code> doesn\'t have the <code>-printf</code> predicate, <code>stat</code> can\'t output NULL bytes, and <code>sed</code> + <code>awk</code> can\'t handle <code>NULL</code>-delimited records.</p>\n<p>Here\'s a solution that <strong>doesn\'t support newlines in paths</strong> but that safeguards against them by filtering them out:</p>\n<pre class="lang-bash prettyprint-override"><code>#!/bin/bash\nn=5\n\nfind . -maxdepth 1 -type f ! -path $\'*\\n*\' -exec stat -f \'%.9Fm %N\' {} + |\nsort -nrt \' \' -k1,1 |\nawk -v n=&quot;$n&quot; -F\'^[^ ]* \' \'NR &gt; n {printf &quot;%s%c&quot;, $2, 0}\' |\nxargs -0 rm -f\n</code></pre>\n<p><sup><strong>note:</strong> I\'m using <code>bash</code> because of the <code>$\'\\n\'</code> notation. For <code>sh</code> you can define a variable containing a literal newline and use it instead.</sup></p>\n<hr />\n<p>Solution for <strong>UNIX &amp; Linux</strong> (inspired from AIX/HP-UX/SunOS/BSD/Linux <code>ls -b</code>):</p>\n<p>Some platforms don\'t provide <code>find -printf</code>, nor <code>stat</code>, nor support <code>NULL</code>-delimited records with <code>stat</code>/<code>sort</code>/<code>awk</code>/<code>sed</code>/<code>xargs</code>. That\'s why using <code>perl</code> is probably the most portable way to tackle the problem, because it is available by default in almost every OS.</p>\n<p>I could have written the whole thing in <code>perl</code> but I didn\'t. I only use it for substituting <code>stat</code> and for encoding-decoding-escaping the filenames. The core logic is the same as the previous solutions and is implemented with POSIX tools.</p>\n<p><strong>note:</strong> <code>perl</code>\'s default <code>stat</code> has a resolution of a second, but starting from <code>perl-5.8.9</code> you can get sub-second resolution with the <code>stat</code> function of the module <code>Time::HiRes</code> (when both the OS and the filesystem support it). That\'s what I\'m using here; if your <code>perl</code> doesn\'t provide it then you can remove the <code>‑MTime::HiRes=stat</code> from the command line.</p>\n<pre class="lang-bash prettyprint-override"><code>n=5\n\nfind . \'(\' -name \'.\' -o -prune \')\' -type f -exec \\\nperl -MTime::HiRes=stat -le \'\n    foreach (@ARGV) {\n        @st = stat($_);\n        if ( @st &gt; 0 ) {\n            s/([\\\\\\n])/sprintf( &quot;\\\\%03o&quot;, ord($1) )/ge;\n            print sprintf( &quot;%.9f %s&quot;, $st[9], $_ );\n        }\n        else { print STDERR &quot;stat: $_: $!&quot;; }\n    }\n\' {} + |\n\nsort -nrt \' \' -k1,1 |\n\nsed -e &quot;1,${n}d&quot; -e \'s/[^ ]* //\' |\n\nperl -l -ne \'\n    s/\\\\([0-7]{3})/chr(oct($1))/ge;\n    s/([&quot;\\n])/&quot;\\\\$1&quot;/g;\n    print &quot;\\&quot;$_\\&quot;&quot;; \n\' |\n\nxargs -E \'\' sh -c \'[ &quot;$#&quot; -gt 0 ] &amp;&amp; rm -f &quot;$@&quot;\' sh\n</code></pre>\n<p><strong>Explanations:</strong></p>\n<ul>\n<li><p>For each file found, the first <code>perl</code> gets the modification time and outputs it along the encoded filename (each <code>newline</code> and <code>backslash</code> characters are replaced with the literals <code>\\n</code> and <code>\\\\</code> respectively).</p>\n</li>\n<li><p>Now each <code>time filename</code> is guaranteed to be single-line, so POSIX <code>sort</code> and <code>sed</code> can safely work with this stream.</p>\n</li>\n<li><p>The second <code>perl</code> decodes the filenames and escapes them for POSIX <code>xargs</code>.</p>\n</li>\n<li><p>Lastly, <code>xargs</code> calls <code>rm</code> for deleting the files. The <code>sh</code> command is a trick that prevents <code>xargs</code> from running <code>rm</code> when there\'s no files to delete.</p>\n</li>\n</ul>\n'}]
['https://Stackoverflow.com/questions/25785', 'https://Stackoverflow.com', 'https://Stackoverflow.com/users/797/']