{"id":35,"date":"2013-05-19T22:53:00","date_gmt":"2013-05-19T12:53:00","guid":{"rendered":""},"modified":"2018-05-22T21:16:44","modified_gmt":"2018-05-22T11:16:44","slug":"bash-functions-for-path-manipulation","status":"publish","type":"post","link":"https:\/\/pbw.id.au\/blog\/2013\/05\/bash-functions-for-path-manipulation\/","title":{"rendered":"Bash functions for path manipulation"},"content":{"rendered":"<div style=\"font-family: Times, 'Times New Roman', serif; font-size: 16px; line-height: 22px;\">\n<p>Here is a set of path manipulation functions that I first wrote in 2000, and have tinkered with on and off since then.<\/p>\n<p>The functions fall into three categories:<\/p>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li>Path stripping<\/li>\n<li>Path building<\/li>\n<li>Path listing<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><!--more--><\/p>\n<ul>\n<li>Path stripping functions\n<ul>\n<li><b>rem_path<\/b>\n<ul>\n<li>Removes the individual path argument(s) from PATH. \u00a0Implemented as <i>rem_vpath PATH args&#8230;<\/i><\/li>\n<\/ul>\n<\/li>\n<li><b>rem_vpath<\/b>\n<ul>\n<li>Removes the individual path argument(s) from\u00a0the path specified by the first argument; e.g. <i>rem_vpath CLASSPATH \/some\/class\/path.jar &#8230;<\/i><\/li>\n<li><b>chk_delim<\/b>\n<ul>\n<li>A subsidiary function of <i>rem_vpath<\/i> which tries to find a safe regular expression delimiter for the <i>sed<\/i> removal code.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<li><b>clean_path<\/b>\n<ul>\n<li>Removes second and subsequent duplicate entries from PATH. Implemented as <i>clean_vpath PATH<\/i><\/li>\n<\/ul>\n<\/li>\n<li><b>clean_vpath<\/b>\n<ul>\n<li>Removes second and subsequent duplicate entries from the path named in the first (and only) argument; e.g <i>clean_vpath CLASSPATH<\/i><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<li>Path building functions\n<ul>\n<li><b>app_path<\/b>\n<ul>\n<li>Appends the argument(s) to PATH. Any duplicates are then removed. Implemented as\u00a0<i>app_vpath PATH args&#8230;<\/i><\/li>\n<\/ul>\n<\/li>\n<li><b>app_vpath<\/b>\n<ul>\n<li>Prepends the second and subsequent arguments(s) to the path named in the first argument. \u00a0Any duplicates are then removed.<br \/>\nE.g.\u00a0\u00a0<i>app_vpath CLASSPATH \/some\/class\/path.jar &#8230;<\/i><\/li>\n<\/ul>\n<\/li>\n<li><b>pre_path<\/b>\n<ul>\n<li><b><span style=\"font-weight: normal;\">Prepends the argument(s) to PATH. The arguments are added in order of arguments.\u00a0Any duplicates are then removed.\u00a0Implemented as<\/span><span style=\"font-weight: normal;\">\u00a0<\/span><i style=\"font-weight: normal;\">pre_vpath PATH args&#8230;<\/i><\/b><\/li>\n<\/ul>\n<\/li>\n<li><b>pre_vpath<\/b>\n<ul>\n<li><b><span style=\"font-weight: normal;\">Prepends the second and subsequent arguments(s) to the path named in the first argument. The paths appear in order of arguments. Any duplicates are then removed.<br \/>\nE.g.\u00a0\u00a0<\/span><i style=\"font-weight: normal;\">pre_vpath CLASSPATH \/some\/class\/path.jar &#8230;<\/i><\/b><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<li>Path listing functions\n<ul>\n<li><b>list_path<\/b>\n<ul>\n<li>Lists the elements of path, one per line. Implemented as <i>list_vpath PATH<\/i><\/li>\n<\/ul>\n<\/li>\n<li><b>list_vpath<\/b>\n<ul>\n<li>Lists the elements of the path named in the first (and only) argument, one per line.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>The file is available <a href=\"http:\/\/pbw.id.au\/src\/sh\/.pathFuncs.sh\">online<\/a>.<\/p>\n<pre style=\"color: blue; font-size: 14px;\"># N.B  The following PATH manipulation functions\n# 1) handle the empty path \"\", but do not equate the empty path with \".\"\n# 2) will fail on all rem_* functions if\n#    ALL of the characters '|' '%' ',' '~' '#' '@'\n#    appear in the path being manipulated.\n#    The chk-separator() function will check for the presence of each character in turn\n#    until it finds one that does not occur.  If all occur, the rem_* functions will not\n#    attempt removal.<\/pre>\n<p># These functions have been written to cope with spaces in the file paths.<\/p>\n<p># Check each possible regular expression delimiter in turn against the first argument.<br \/>\n# Set the variable dlim to the first delimiter that does NOT occur, or &#8220;&#8221; if all occur.<br \/>\nchk_delim() {<br \/>\nlocal c z<br \/>\nif [ $# -lt 1 ]; then return; fi<br \/>\nfor c in &#8216;|&#8217; &#8216;%&#8217; &#8216;,&#8217; &#8216;~&#8217; &#8216;#&#8217; &#8216;@&#8217;<br \/>\ndo<br \/>\nz=`expr &#8220;$1&#8221; : &#8216;.*'&#8221;$c&#8221;&#8216;.*&#8217;`<br \/>\nif [ $z -eq 0 ]; then dlim=$c; break; fi<br \/>\ndone<br \/>\nif [ &#8220;$c&#8221; != &#8220;$dlim&#8221; ]<br \/>\nthen<br \/>\ndlim=&#8221;&#8221;<br \/>\nfi<br \/>\n}<\/p>\n<p># Clear path element given as arg from PATH<br \/>\n# N.B. path element MUST NOT contain a vertical bar `|&#8217;<br \/>\nrem_path() {<br \/>\nif [ $# -lt 1 ]; then return; fi<br \/>\nrem_vpath PATH &#8220;$@&#8221;<br \/>\n}<\/p>\n<p># Clear path element(s) given as arg 2 (3 &#8230;) from the path named in arg1<br \/>\n# N.B. path element MUST NOT contain a vertical bar `|&#8217;<br \/>\nrem_vpath() {<br \/>\nlocal pathvalue pathname rem_el dlim tmp_path<br \/>\nif [ $# -lt 2 ]; then return; fi<br \/>\neval pathvalue=&#8221;$$1&#8243;<br \/>\nchk_delim &#8220;$pathvalue&#8221;<br \/>\n# If existing pathvalue cannot be edited, return immediately<br \/>\nif [ &#8220;$dlim&#8221; == &#8220;&#8221; ]; then return; fi<br \/>\npathname=$1<br \/>\nshift<br \/>\nwhile [ $# -ge 1 ]; do<br \/>\nrem_el=&#8221;$1&#8243;<br \/>\nshift<br \/>\ntmp_path=&#8221;$pathvalue&#8221;&#8221;$rem_el&#8221;<br \/>\nchk_delim &#8220;$tmp_path&#8221;<br \/>\nif [ &#8220;$dlim&#8221; == &#8220;&#8221; ]; then continue; fi<br \/>\npathvalue=`echo $pathvalue|<br \/>\nsed &#8216;:loop<br \/>\n{s'&#8221;$dlim&#8221;&#8216;:'&#8221;$rem_el&#8221;&#8216;:'&#8221;$dlim&#8221;&#8216;:'&#8221;$dlim&#8221;&#8216;g<br \/>\nt loop<br \/>\n}<br \/>\ns'&#8221;$dlim&#8221;&#8216;^'&#8221;$rem_el&#8221;&#8216;:'&#8221;$dlim$dlim&#8221;&#8216;<br \/>\ns'&#8221;$dlim&#8221;&#8216;:'&#8221;$rem_el&#8221;&#8216;$'&#8221;$dlim$dlim&#8221;&#8216;<br \/>\ns'&#8221;$dlim&#8221;&#8216;^'&#8221;$rem_el&#8221;&#8216;$'&#8221;$dlim$dlim&#8221;&#8221;`<br \/>\ndone<br \/>\neval $pathname=&#8221;$pathvalue&#8221;<br \/>\n}<\/p>\n<p># To path named in $1, append path element arg(s), replacing all existing<br \/>\n# instances in the named path<br \/>\n# N.B. path elements MUST NOT contain a vertical bar `|&#8217;<br \/>\napp_vpath() {<br \/>\nlocal pathname pathvalue el<br \/>\npathname=$1; shift<br \/>\nfor el<br \/>\ndo<br \/>\nrem_vpath $pathname &#8220;$el&#8221;<br \/>\neval pathvalue=&#8221;$$pathname&#8221;<br \/>\npathvalue=&#8221;${pathvalue:+${pathvalue}:}$el&#8221;<br \/>\neval ${pathname}=&#8221;$pathvalue&#8221;<br \/>\ndone<br \/>\nexport $pathname<br \/>\n}<\/p>\n<p># Append path element(s) given as args to PATH, replacing all existing<br \/>\n# instances in the PATH<br \/>\n# N.B. path elements MUST NOT contain a vertical bar `|&#8217;<br \/>\napp_path() {<br \/>\napp_vpath PATH &#8220;$@&#8221;<br \/>\n}<\/p>\n<p># To path named in $1, prepend path element arg(s), replacing all existing<br \/>\n# instances in the named path. Elements will be appear in the PATH in<br \/>\n# argument order.<br \/>\n# N.B. path elements MUST NOT contain a vertical bar `|&#8217;<br \/>\npre_vpath() {<br \/>\nlocal pathname pathvalue sptr element<br \/>\npathname=$1; shift<br \/>\nsptr=0<br \/>\nwhile [ $# -gt 0 ]<br \/>\ndo<br \/>\neval local elstack$((++sptr))=&#8221;$1&#8243;<br \/>\nshift<br \/>\ndone<br \/>\nwhile [ $sptr -gt 0 ]<br \/>\ndo<br \/>\neval element=${elstack$((sptr&#8211;))}<br \/>\nrem_vpath $pathname &#8220;$element&#8221;<br \/>\neval pathvalue=&#8221;$$pathname&#8221;<br \/>\npathvalue=&#8221;$element${pathvalue:+:${pathvalue}}&#8221;<br \/>\neval ${pathname}=&#8221;$pathvalue&#8221;<br \/>\ndone<br \/>\nexport $pathname<br \/>\n}<\/p>\n<p># Prepend path element(s) given as args to PATH, replacing all existing<br \/>\n# instances in the PATH. N.B. elements will be appear in the PATH in<br \/>\n# REVERSE argument order.<br \/>\n# N.B. path elements MUST NOT contain a vertical bar `|&#8217;<br \/>\npre_path() {<br \/>\npre_vpath PATH &#8220;$@&#8221;<br \/>\n}<\/p>\n<p># Clean a path &#8211; run pre_vpath with every current element of the path<br \/>\n# in reverse order<br \/>\n# This removes all duplicates in the path, and leaves the first instance<br \/>\n# of a path element in its original relative place &#8211; later ones are deleted.<br \/>\nclean_vpath() {<br \/>\nlocal pathname pathvalue<br \/>\npathname=$1; shift<br \/>\n# pathname contains the name of the path<br \/>\neval pathvalue=&#8221;$$pathname&#8221;<br \/>\n# pathvalue contains the value of the path<br \/>\npathvalue=`echo &#8220;$pathvalue&#8221;|sed &#8216;s\/^\/&#8221;\/<br \/>\ns\/:\/&#8221; &#8220;\/g<br \/>\ns\/$\/&#8221;\/&#8217;`<br \/>\neval pre_vpath $pathname &#8220;$pathvalue&#8221;<br \/>\n}<\/p>\n<p>clean_path() {<br \/>\nclean_vpath PATH<br \/>\n}<\/p>\n<p># This prints out the elements of the path named in its first argument,<br \/>\n# one per line.<br \/>\nlist_vpath() {<br \/>\nif [ $# -lt 1 ]; then return; fi<br \/>\nlocal pathname pathvalue<br \/>\npathname=$1; shift<br \/>\neval pathvalue=&#8221;$$pathname&#8221;<br \/>\necho &#8220;$pathvalue&#8221;|sed &#8216;:loop<br \/>\n{h<br \/>\ns\/:.*\/\/<br \/>\np<br \/>\ng<br \/>\ns\/[^:]*:\/\/<br \/>\nt loop<br \/>\nd<br \/>\n}&#8217;<br \/>\n}<\/p>\n<p># This prints the elements of PATH, one per line<br \/>\nlist_path() {<br \/>\nlist_vpath PATH<br \/>\n}<\/p>\n<p>[ -n &#8220;$BASH&#8221; ] &amp;&amp;<br \/>\nexport -f chk_delim rem_path rem_vpath app_path app_vpath pre_path pre_vpath clean_path clean_vpath list_path list_vpath<\/p>\n<pre style=\"color: blue; font-size: 14px;\"><\/pre>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Here is a set of path manipulation functions that I first wrote in 2000, and have tinkered with on and off since then. The functions fall into three categories: Path stripping Path building Path listing<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[19],"tags":[],"class_list":["post-35","post","type-post","status-publish","format-standard","hentry","category-code"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p8SCfl-z","jetpack-related-posts":[{"id":33,"url":"https:\/\/pbw.id.au\/blog\/2014\/06\/zargrep-grep-files-in-a-zip-archive\/","url_meta":{"origin":35,"position":0},"title":"zargrep: grep files in a zip archive","author":"pbw","date":"Sat 21st Jun '14","format":false,"excerpt":"How do you search for strings within a zip archive? I'm tinkering with EPUB3 files, and I wanted to be able to find certain strings within .epub files, so I had a look around, and I immediately found zgrep and family. The trouble was that zgrep assumes a single zipped\u2026","rel":"","context":"In &quot;Code&quot;","block_context":{"text":"Code","link":"https:\/\/pbw.id.au\/blog\/category\/code\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":31,"url":"https:\/\/pbw.id.au\/blog\/2015\/03\/help-for-digest-checking\/","url_meta":{"origin":35,"position":1},"title":"Help for digest checking","author":"pbw","date":"Fri 20th Mar '15","format":false,"excerpt":"Updated 2018-02-14 It's pretty important to check the digests of software you download. \u00a0When a downloaded file is accompanied by a signature file, for example a gnupg .asc file, you can verify the signature with various tools. \u00a0Often though, a download site will include the MD5 or SHA1 digest hash\u2026","rel":"","context":"In &quot;Code&quot;","block_context":{"text":"Code","link":"https:\/\/pbw.id.au\/blog\/category\/code\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":32,"url":"https:\/\/pbw.id.au\/blog\/2015\/02\/monad-thats-a-wrap\/","url_meta":{"origin":35,"position":2},"title":"Monad; that&#8217;s a wrap!","author":"pbw","date":"Mon 16th Feb '15","format":false,"excerpt":"Just like everybody else who starts to look at monads, I found it was like coming to the face of a sheer cliff. \u00a0Let me qualify that: just like every other programmer who is not a mathematician (and that's most of us). \u00a0I am looking at monads in the context\u2026","rel":"","context":"In &quot;Code&quot;","block_context":{"text":"Code","link":"https:\/\/pbw.id.au\/blog\/category\/code\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":60,"url":"https:\/\/pbw.id.au\/blog\/2013\/01\/does-intelligent-design-subvert-faith\/","url_meta":{"origin":35,"position":3},"title":"Does Intelligent Design subvert faith?","author":"pbw","date":"Sat 5th Jan '13","format":false,"excerpt":"Spengler (David Goldman) posted an article in the Asia Times Online, titled Why 'Intelligent Design' subverts faith. What follows is my reply. Quotes from Goldman's article are in italics, for the most part. Spengler quotes from an article by David Bentley Hart, hence the reference. The workings of nature are\u2026","rel":"","context":"In &quot;Belief &amp; knowledge&quot;","block_context":{"text":"Belief &amp; knowledge","link":"https:\/\/pbw.id.au\/blog\/category\/belief\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":56,"url":"https:\/\/pbw.id.au\/blog\/2014\/12\/schrodingers-baby\/","url_meta":{"origin":35,"position":4},"title":"Schr\u00f6dinger&#8217;s Baby","author":"pbw","date":"Fri 26th Dec '14","format":false,"excerpt":"Ms Schr\u00f6dinger is pregnant; and Ms Schr\u00f6dinger is not. Her pregnancy confirmed, she experienced joy or resignation; and she cursed the inconvenience or shrugged her shoulders.\u00a0 She sought advice from her friends about obstetricians; and she sought advice about clinics and prescription drugs.\u00a0 She has expectations of new life; and\u2026","rel":"","context":"In &quot;The culture&quot;","block_context":{"text":"The culture","link":"https:\/\/pbw.id.au\/blog\/category\/culture\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":943,"url":"https:\/\/pbw.id.au\/blog\/2020\/12\/deadlock\/","url_meta":{"origin":35,"position":5},"title":"Deadlock","author":"admin","date":"Fri 11th Dec '20","format":false,"excerpt":"[Published at Catallaxy Files 27\/11\/2020] Edsgar Dijkstra became a programmer in 1951. He is one of the early giants in a field that saw an unprecedented explosion of intellectual activity. When multi-processing came to computing, the phenomenon of deadlock began to make a pest of itself. Processes would just sieze\u2026","rel":"","context":"In &quot;Catallaxy Files&quot;","block_context":{"text":"Catallaxy Files","link":"https:\/\/pbw.id.au\/blog\/category\/publications\/catallaxy-files\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/pbw.id.au\/blog\/wp-json\/wp\/v2\/posts\/35","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/pbw.id.au\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/pbw.id.au\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/pbw.id.au\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/pbw.id.au\/blog\/wp-json\/wp\/v2\/comments?post=35"}],"version-history":[{"count":3,"href":"https:\/\/pbw.id.au\/blog\/wp-json\/wp\/v2\/posts\/35\/revisions"}],"predecessor-version":[{"id":572,"href":"https:\/\/pbw.id.au\/blog\/wp-json\/wp\/v2\/posts\/35\/revisions\/572"}],"wp:attachment":[{"href":"https:\/\/pbw.id.au\/blog\/wp-json\/wp\/v2\/media?parent=35"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/pbw.id.au\/blog\/wp-json\/wp\/v2\/categories?post=35"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/pbw.id.au\/blog\/wp-json\/wp\/v2\/tags?post=35"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}