{"id":10590,"date":"2015-01-25T11:56:38","date_gmt":"2015-01-25T16:56:38","guid":{"rendered":"http:\/\/mjtsai.com\/blog\/?p=10590"},"modified":"2015-01-26T16:33:10","modified_gmt":"2015-01-26T21:33:10","slug":"deleting-folders-using-the-find-command","status":"publish","type":"post","link":"https:\/\/mjtsai.com\/blog\/2015\/01\/25\/deleting-folders-using-the-find-command\/","title":{"rendered":"Deleting Folders Using the &ldquo;find&rdquo; Command"},"content":{"rendered":"<p>I&rsquo;ve seen lots of Web pages about how to use the Unix <tt>find<\/tt> command to delete files or directories with a certain name. I do this pretty frequently. For example, the <a href=\"https:\/\/www.gnu.org\/software\/make\/\">Makefile<\/a> for deploying my apps deletes the <tt>Headers<\/tt> folders inside of the embedded frameworks. It also has legacy lines for deleting <tt>CVS<\/tt> and <tt>.svn<\/tt> folders. The standard advice seems to be to write something like this:<\/p>\n<pre>find MyApp.app -name Headers -type d -delete<\/pre>\n<p>This looks inside the <tt>MyApp.app<\/tt> folder for items named <tt>Headers<\/tt> that are directories and deletes them. This works with <a href=\"http:\/\/www.gnu.org\/software\/findutils\/\">GNU find<\/a>, but in the <a href=\"https:\/\/developer.apple.com\/library\/mac\/documentation\/Darwin\/Reference\/ManPages\/man1\/find.1.html\">BSD version of find<\/a> (which ships with Macs) the <code>-delete<\/code> option only works with files and empty directories. Confusingly, it won&rsquo;t report failure for a non-empty directory; it will just leave it there. The common solution is to use a command like this:<\/p>\n<pre>find MyApp.app -name Headers -type d -exec rm -rf \"{}\" \\;<\/pre>\n<p>This executes the <tt>rm -r {}<\/tt> command for each found item, with the <code>{}<\/code> replaced by the item&rsquo;s path. The <code>\\;<\/code> marks the end of the command and is escaped so that the shell sends the <code>;<\/code> to <tt>find<\/tt> instead of interpreting it as a command separator. The problem with this version is that it can result in &ldquo;No such file or directory&rdquo;&nbsp;errors. <tt>find<\/tt> will execute the command for a folder (thus deleting it), then try to recurse into that folder and complain that it doesn&rsquo;t exist. <tt>find<\/tt> succeeded in that all the directories did get deleted&mdash;it doesn&rsquo;t stop when it encounters the first missing directory&mdash;but it reports a failing exit status that can halt your shell script or Makefile.<\/p>\n<p>A common way to work around this is to silence the errors. You can use:<\/p>\n<pre>find MyApp.app -name Headers -type d -exec rm -rf \"{}\" \\; || true<\/pre>\n<p>to make sure that the combined command exits with success. Or, in a Makefile, you can use:<\/p>\n<pre>-find MyApp.app -name Headers -type d -exec rm -rf \"{}\" \\;<\/pre>\n<p>to tell <tt>make<\/tt> to ignore errors. In both cases, the command will succeed, but it will still print &ldquo;No such file or directory&rdquo;. So sometimes people silence the error:<\/p>\n<pre>-find MyApp.app -name Headers -type d -exec rm -rf \"{}\" \\; 2&gt;\/dev\/null<\/pre>\n<p>My preferred solution is to prevent the error in the first place. You can tell <tt>find<\/tt> to do a <a href=\"http:\/\/en.wikipedia.org\/wiki\/Tree_traversal#Post-order\">post-order<\/a> traversal instead of a <a href=\"http:\/\/en.wikipedia.org\/wiki\/Tree_traversal#Pre-order\">pre-order<\/a> one. In other words, it will recurse first and delete later. This is done by specifying the <code>-d<\/code> option:<\/p>\n<pre>find -d MyApp.app -name Headers -type d -exec rm -rf \"{}\" \\;<\/pre>\n<p>You also optimize this somewhat by using <code>+<\/code> instead of <code>\\;<\/code>. This causes <tt>find<\/tt> to send multiple items to a single invocation of the <tt>rm<\/tt> command. I&rsquo;ve also found that, at least with Bash, it is not necessary to quote the braces. This command looks cleaner and works well:<\/p>\n<pre>find -d MyApp.app -name Headers -type d -exec rm -rf {} +<\/pre>\n<p>Another way to do essentially the same thing is to use <tt>xargs<\/tt>:<\/p>\n<pre>find -d MyApp.app -name Headers -type d -print0 | xargs -0 rm -rf<\/pre>\n<p>In practice, it seems that if you use <tt>xargs<\/tt> you don&rsquo;t need <code>-d<\/code>. Perhaps this is because a certain amount of <tt>find<\/tt> output is buffered before <tt>xargs<\/tt> begins deleting. However, I think it is more correct to leave it in.<\/p>\n<p>Update (2015-01-26): As noted in the comments, you can use <code>-prune<\/code> (instead of <code>-d<\/code>) to tell <tt>find<\/tt> not to recurse into the matching folders that will be deleted anyway:<\/p>\n<pre>find MyApp.app -name Headers -type d -prune -exec rm -rf {} +<\/pre>","protected":false},"excerpt":{"rendered":"<p>I&rsquo;ve seen lots of Web pages about how to use the Unix find command to delete files or directories with a certain name. I do this pretty frequently. For example, the Makefile for deploying my apps deletes the Headers folders inside of the embedded frameworks. It also has legacy lines for deleting CVS and .svn [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"apple_news_api_created_at":"","apple_news_api_id":"","apple_news_api_modified_at":"","apple_news_api_revision":"","apple_news_api_share_url":"","apple_news_coverimage":0,"apple_news_coverimage_caption":"","apple_news_is_hidden":false,"apple_news_is_paid":false,"apple_news_is_preview":false,"apple_news_is_sponsored":false,"apple_news_maturity_rating":"","apple_news_metadata":"\"\"","apple_news_pullquote":"","apple_news_pullquote_position":"","apple_news_slug":"","apple_news_sections":"\"\"","apple_news_suppress_video_url":false,"apple_news_use_image_component":false,"footnotes":""},"categories":[2],"tags":[767,30,318,163],"class_list":["post-10590","post","type-post","status-publish","format-standard","hentry","category-technology","tag-bash","tag-mac","tag-terminal","tag-unix"],"apple_news_notices":[],"_links":{"self":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/10590","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/comments?post=10590"}],"version-history":[{"count":7,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/10590\/revisions"}],"predecessor-version":[{"id":10605,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/10590\/revisions\/10605"}],"wp:attachment":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/media?parent=10590"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/categories?post=10590"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/tags?post=10590"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}