Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>Addressing the proposed solution from dmckee:</p> <ol> <li>While some versions of Bash may allow hyphens in function names, others (MacOS X) do not.</li> <li>I don't see a need to use return immediately before the end of the function.</li> <li>I don't see the need for all the semi-colons.</li> <li>I don't see why you have path-element-by-pattern export a value. Think of <code>export</code> as equivalent to setting (or even creating) a global variable - something to be avoided whenever possible.</li> <li>I'm not sure what you expect '<code>replace-path PATH $PATH /usr</code>' to do, but it does not do what I would expect.</li> </ol> <p>Consider a PATH value that starts off containing:</p> <pre><code>. /Users/jleffler/bin /usr/local/postgresql/bin /usr/local/mysql/bin /Users/jleffler/perl/v5.10.0/bin /usr/local/bin /usr/bin /bin /sw/bin /usr/sbin /sbin </code></pre> <p>The result I got (from '<code>replace-path PATH $PATH /usr</code>') is:</p> <pre><code>. /Users/jleffler/bin /local/postgresql/bin /local/mysql/bin /Users/jleffler/perl/v5.10.0/bin /local/bin /bin /bin /sw/bin /sbin /sbin </code></pre> <p>I would have expected to get my original path back since /usr does not appear as a (complete) path element, only as part of a path element.</p> <p>This can be fixed in <code>replace-path</code> by modifying one of the <code>sed</code> commands:</p> <pre><code>export $path=$(echo -n $list | tr ":" "\n" | sed "s:^$removestr\$:$replacestr:" | tr "\n" ":" | sed "s|::|:|g") </code></pre> <p>I used ':' instead of '|' to separate parts of the substitute since '|' could (in theory) appear in a path component, whereas by definition of PATH, a colon cannot. I observe that the second <code>sed</code> could eliminate the current directory from the middle of a PATH. That is, a legitimate (though perverse) value of PATH could be:</p> <pre><code>PATH=/bin::/usr/local/bin </code></pre> <p>After processing, the current directory would no longer be on the PATH.</p> <p>A similar change to anchor the match is appropriate in <code>path-element-by-pattern</code>:</p> <pre><code>export $target=$(echo -n $list | tr ":" "\n" | grep -m 1 "^$pat\$") </code></pre> <p>I note in passing that <code>grep -m 1</code> is not standard (it is a GNU extension, also available on MacOS X). And, indeed, the<code>-n</code> option for <code>echo</code> is also non-standard; you would be better off simply deleting the trailing colon that is added by virtue of converting the newline from echo into a colon. Since path-element-by-pattern is used just once, has undesirable side-effects (it clobbers any pre-existing exported variable called <code>$removestr</code>), it can be replaced sensibly by its body. This, along with more liberal use of quotes to avoid problems with spaces or unwanted file name expansion, leads to:</p> <pre><code># path_tools.bash # # A set of tools for manipulating ":" separated lists like the # canonical $PATH variable. # # /bin/sh compatibility can probably be regained by replacing $( ) # style command expansion with ` ` style ############################################################################### # Usage: # # To remove a path: # replace_path PATH $PATH /exact/path/to/remove # replace_path_pattern PATH $PATH &lt;grep pattern for target path&gt; # # To replace a path: # replace_path PATH $PATH /exact/path/to/remove /replacement/path # replace_path_pattern PATH $PATH &lt;target pattern&gt; /replacement/path # ############################################################################### # Remove or replace an element of $1 # # $1 name of the shell variable to set (e.g. PATH) # $2 a ":" delimited list to work from (e.g. $PATH) # $3 the precise string to be removed/replaced # $4 the replacement string (use "" for removal) function replace_path () { path=$1 list=$2 remove=$3 replace=$4 # Allowed to be empty or unset export $path=$(echo "$list" | tr ":" "\n" | sed "s:^$remove\$:$replace:" | tr "\n" ":" | sed 's|:$||') } # Remove or replace an element of $1 # # $1 name of the shell variable to set (e.g. PATH) # $2 a ":" delimited list to work from (e.g. $PATH) # $3 a grep pattern identifying the element to be removed/replaced # $4 the replacement string (use "" for removal) function replace_path_pattern () { path=$1 list=$2 removepat=$3 replacestr=$4 # Allowed to be empty or unset removestr=$(echo "$list" | tr ":" "\n" | grep -m 1 "^$removepat\$") replace_path "$path" "$list" "$removestr" "$replacestr" } </code></pre> <p>I have a Perl script called <code>echopath</code> which I find useful when debugging problems with PATH-like variables:</p> <pre><code>#!/usr/bin/perl -w # # "@(#)$Id: echopath.pl,v 1.7 1998/09/15 03:16:36 jleffler Exp $" # # Print the components of a PATH variable one per line. # If there are no colons in the arguments, assume that they are # the names of environment variables. @ARGV = $ENV{PATH} unless @ARGV; foreach $arg (@ARGV) { $var = $arg; $var = $ENV{$arg} if $arg =~ /^[A-Za-z_][A-Za-z_0-9]*$/; $var = $arg unless $var; @lst = split /:/, $var; foreach $val (@lst) { print "$val\n"; } } </code></pre> <p>When I run the modified solution on the test code below:</p> <pre><code>echo xpath=$PATH replace_path xpath $xpath /usr echopath $xpath echo xpath=$PATH replace_path_pattern xpath $xpath /usr/bin /work/bin echopath xpath echo xpath=$PATH replace_path_pattern xpath $xpath "/usr/.*/bin" /work/bin echopath xpath </code></pre> <p>The output is:</p> <pre><code>. /Users/jleffler/bin /usr/local/postgresql/bin /usr/local/mysql/bin /Users/jleffler/perl/v5.10.0/bin /usr/local/bin /usr/bin /bin /sw/bin /usr/sbin /sbin . /Users/jleffler/bin /usr/local/postgresql/bin /usr/local/mysql/bin /Users/jleffler/perl/v5.10.0/bin /usr/local/bin /work/bin /bin /sw/bin /usr/sbin /sbin . /Users/jleffler/bin /work/bin /usr/local/mysql/bin /Users/jleffler/perl/v5.10.0/bin /usr/local/bin /usr/bin /bin /sw/bin /usr/sbin /sbin </code></pre> <p>This looks correct to me - at least, for my definition of what the problem is.</p> <p>I note that <code>echopath LD_LIBRARY_PATH</code> evaluates <code>$LD_LIBRARY_PATH</code>. It would be nice if your functions were able to do that, so the user could type:</p> <pre><code>replace_path PATH /usr/bin /work/bin </code></pre> <p>That can be done by using:</p> <pre><code>list=$(eval echo '$'$path) </code></pre> <p>This leads to this revision of the code:</p> <pre><code># path_tools.bash # # A set of tools for manipulating ":" separated lists like the # canonical $PATH variable. # # /bin/sh compatibility can probably be regained by replacing $( ) # style command expansion with ` ` style ############################################################################### # Usage: # # To remove a path: # replace_path PATH /exact/path/to/remove # replace_path_pattern PATH &lt;grep pattern for target path&gt; # # To replace a path: # replace_path PATH /exact/path/to/remove /replacement/path # replace_path_pattern PATH &lt;target pattern&gt; /replacement/path # ############################################################################### # Remove or replace an element of $1 # # $1 name of the shell variable to set (e.g. PATH) # $2 the precise string to be removed/replaced # $3 the replacement string (use "" for removal) function replace_path () { path=$1 list=$(eval echo '$'$path) remove=$2 replace=$3 # Allowed to be empty or unset export $path=$(echo "$list" | tr ":" "\n" | sed "s:^$remove\$:$replace:" | tr "\n" ":" | sed 's|:$||') } # Remove or replace an element of $1 # # $1 name of the shell variable to set (e.g. PATH) # $2 a grep pattern identifying the element to be removed/replaced # $3 the replacement string (use "" for removal) function replace_path_pattern () { path=$1 list=$(eval echo '$'$path) removepat=$2 replacestr=$3 # Allowed to be empty or unset removestr=$(echo "$list" | tr ":" "\n" | grep -m 1 "^$removepat\$") replace_path "$path" "$removestr" "$replacestr" } </code></pre> <p>The following revised test now works too:</p> <pre><code>echo xpath=$PATH replace_path xpath /usr echopath xpath echo xpath=$PATH replace_path_pattern xpath /usr/bin /work/bin echopath xpath echo xpath=$PATH replace_path_pattern xpath "/usr/.*/bin" /work/bin echopath xpath </code></pre> <p>It produces the same output as before.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload