{"id":897,"date":"2021-01-27T15:32:37","date_gmt":"2021-01-27T22:32:37","guid":{"rendered":"http:\/\/10.0.1.201\/?p=897"},"modified":"2025-11-30T12:24:46","modified_gmt":"2025-11-30T19:24:46","slug":"shell-script-to-re-format-poor-date-format-in-filenames","status":"publish","type":"post","link":"https:\/\/strawhousepig.net\/wordpress\/2021\/01\/27\/shell-script-to-re-format-poor-date-format-in-filenames\/","title":{"rendered":"Shell script to re-format poor date format in filenames"},"content":{"rendered":"<p>New, more-good version here: <a href=\"http:\/\/strawhousepig.net\/new-script-to-re-write-dates-in-file-names\/\">New script to re-write dates in file names<\/a><\/p>\n<p><ins datetime=\"2021-02-03T22:20:58+00:00\">After testing and some using, this is going to change to just write the <code>mv<\/code> statements to a file and to accept an argument to run them instead. No logging since that will serve the same purpose. This post does not contain the final version. It&#8217;s here for the sake of history.<\/ins><\/p>\n<p><ins datetime=\"2021-01-30T02:29:22+00:00\">And boom. Or so I hope. This *should* handle dates as &#8220;01.02.03&#8221; and the equivalent &#8220;01.02.2003&#8221;.<\/ins><br \/>\n<del datetime=\"2021-01-30T17:05:05+00:00\">To-do: Check for administrator privileges or<\/del> just write the log to <code>~\/<\/code>. MacOS (nee OS X (nee Mac OS)) has a default user log directory, but GNU does not. I suppose we could check for &#8220;~\/.log\/&#8221; and <code>mkdir<\/code> if false&#8230;<\/p>\n<p>Title explains it pretty well. Uses <code>egrep<\/code> to find date, <code>sed<\/code> to edit it, <code>mv<\/code> to rename, and <code>read<\/code> for user confirmation prompt. Which, btw, can&#8217;t be done if you are already doing a read without feeding the new <code>read<\/code> from somewhere other than <code>stdin<\/code>. Hence the <code>&lt; \/dev\/tty<\/code> at that point in the script. So many strangers to thank for sharing their knowledge online. Thanks, strangers! That should do it.<\/p>\n<p>Why this exists is I had a co-worker who added dates to tons of files in the format &#8220;01.02.03&#8221;, which resulted in files named &#8220;Example filename 01.02.03.xmpl&#8221; Same for phone numbers. Clearly this is wrong and something must be done about it (now that new ones won&#8217;t be cropping up since he.is.gone). You may be asking yourself how he got away with not getting an e-mailed file bounced back for multiple extensions? My educated guess is that he did and kept doing it the dumb way despite that.<\/p>\n<p>Corrects the above format to &#8220;+%Y-%m-%d&#8221; (YYYY-MM-DD).<\/p>\n<p>This could probably be done with fewer lines or overall be more betterer, but this is like my first whole shell script. Maybe my years of AppleScript are showing. *meh*<br \/>\n<!--more--><\/p>\n<pre>#!\/bin\/sh\n# NOTICE: This is recursive.\n\n#### SETTINGS ####\n\n# Behavior:\n# prompt = Prompt to confirm each file change, run if yes and log the new name (if enabled).\n# build = Build a file in 'pwd' that contains all 'mv' statements to peruse and\/or run later.\n# now = Run each mv statement after it is generated, log new file name (if enabled).\nbehavior=build\n\n# Name of log file. Leave blank to disable logging.\nloglog=mvdate.log\n\n# Directory (must exist) to store the log. Leave blank to use the defaults below.\nlogdir=\n\n#### END OF SETTINGS ####\n\n# We want to log all files altered in case something shouldn't have been.\n# Apparently macOS (nee OS X) is the only Unix with a default user log directory.\n# GNU doesn't have one either. Lovely. :\/\nif [ ! \"$logdir\" ]; then\nif [ -d ~\/Library\/Logs ]\nthen\nlogdir=~\/Library\/Logs\nelse\nlogdir=~\/.log\nif [ ! -d $logdir ]; then mkdir $logdir; fi\nfi\nfi\nif [ \"$loglog\" ]; then\nmlog=$logdir\/$loglog\necho \"NOTICE: This script writes time ran and filenames altered to $mlog\"\necho `date` &gt;&gt; $mlog\nfi\n\n# Here, 'tail' reverses the order of 'find' after 'egrep' filters the result.\n# That way files in a directory are renamed before the directory is.\n# 'find' might be able to use the regex, but I couldn't work it out. YMMV.\n# This only looks for the pattern at the end of the line or just before an extension.\nfind \"`pwd`\" | egrep '([0-9]{2}[.-\/,]){2}[0-9]{2,4}(\\.[[:alnum:]]+)?$' | tail -r | while read f\ndo\n\n# Escape certain characters so they don't wreck the 'mv' statement later.\n# double quote, single quote, parens, dollar sign, [space]\n# Single quote and parens are escaped for the shell after breaking out of the 'sed' statement.\nf=$(echo \"$f\" | sed -E 's\/([\"'\\'\\(\\)\\&amp;'[:space:]$])\/\\\\\\1\/g')\n\n# Suck out the last instance of our hated date pattern (00.00.00 or 00.00.0000).\n# Also replace dashes, slashes, and errant commas for dots because we've come too far not to.\nd=$(echo $f | egrep -o '([0-9]{1,2}[.-\/,]){2}[0-9]{2,4}' | tail -1 | sed -E 's\/[-\/,]\/\\.\/g')\nif [ \"$d\" != \"\" ]; then\n\n# 'date' will not accept a 2 OR 4 digit year.\nif [ $(echo $d | egrep -o \"[0-9]{4}$\") ]; then\ndform=\"%m.%d.%Y\"; else\ndform=\"%m.%d.%y\"\nfi\nd2=\"$(date -j -f \"$dform\" \"$d\" \"+%Y-%m-%d\")\"\n\n# This is the 'sed' that finds the date and replaces it with what we made just above.\n# It looks for the pattern at the end of the line (path) but includes the extension if there.\n# It is possible that a version number of some sort will also match. This is why it prompts.\n# Also possible to do away with the EOL in the regex and just go for the pattern.\nf2=\"$(echo \"$f\" | sed -E 's\/([0-9]{1,2}[.,-]){2}[0-9]{2,4}(\\.[[:alnum:]]+)?$\/'$d2'\\2\/')\"\n\n# And double spaces....fml.\n# Which won't work unless we start over and look for them at the tail of the path.\n# f2=\"$(echo \"$f2\" | sed 's\/\\ \\ \/\\ \/g')\"\nm=\"mv -- $f $f2\"\n\ncase \"$behavior\" in\n\"prompt\")\n# We echo the 'mv' statement for approval to be sure it's doing what we want.\n# This 'read' here has to come from somewhere other than stdin.e\necho \"$m\"\nread -r -p \"Run 'mv' (as shown)? [y,n] \" response &lt; \/dev\/tty if [[ $response =~ ^([yY])$ ]]; then eval $m if [ \"$loglog\" ]; then echo $f2 &gt;&gt; $mlog; fi\nfi\n;;\n\"build\")\n# Add every 'mv' statement to a file for later evaluation.\necho \"$m\" &gt;&gt; \"!items to rename.txt\"\n;;\n# Run the 'mv' statements as they are created.\n\"now\")\neval $m\nif [ \"$loglog\" ]; then echo $f2 &gt;&gt; $mlog; fi\n;;\nesac\nfi\ndone\n\n# Add a line between runs.\nif [ \"$loglog\" ]\nthen\nprintf \\\\n &gt;&gt; $mlog\nfi<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>New, more-good version here: New script to re-write dates in file names After testing and some using, this is going to change to just write the mv statements to a file and to accept an argument to run them instead. No logging since that will serve the same purpose. This post does not contain the [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[18,24,28,33,34],"class_list":["post-897","post","type-post","status-publish","format-standard","hentry","category-code","tag-gnulinux","tag-macos","tag-os-x","tag-script","tag-shell-script"],"_links":{"self":[{"href":"https:\/\/strawhousepig.net\/wordpress\/wp-json\/wp\/v2\/posts\/897","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/strawhousepig.net\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/strawhousepig.net\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/strawhousepig.net\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/strawhousepig.net\/wordpress\/wp-json\/wp\/v2\/comments?post=897"}],"version-history":[{"count":1,"href":"https:\/\/strawhousepig.net\/wordpress\/wp-json\/wp\/v2\/posts\/897\/revisions"}],"predecessor-version":[{"id":1060,"href":"https:\/\/strawhousepig.net\/wordpress\/wp-json\/wp\/v2\/posts\/897\/revisions\/1060"}],"wp:attachment":[{"href":"https:\/\/strawhousepig.net\/wordpress\/wp-json\/wp\/v2\/media?parent=897"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/strawhousepig.net\/wordpress\/wp-json\/wp\/v2\/categories?post=897"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/strawhousepig.net\/wordpress\/wp-json\/wp\/v2\/tags?post=897"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}