0

For example, I want to find all symlinks that reference to particular binary in all directories that belong to system $PATH. This can be successfully achieved with manual specification of all directories:

sudo find ~/bin /home/samokat/.local/bin /home/samokat/bin /usr/local/sbin /usr/local/bin /usr/sbin /usr/bin /sbin /bin /snap/bin -lname /opt/openoffice4/program/soffice

But when I'm trying to use command with $PATH expansion:

sudo find `echo $PATH | tr ':' ' '` -lname /opt/openoffice4/program/soffice

I get error and result:

find: ‘~/bin’: No such file or directory
/usr/bin/soffice.link-to-openoffice-bak
echo $PATH | tr ':' ' '

outputs correct path:

~/bin /home/samokat/.local/bin /home/samokat/bin /usr/local/sbin /usr/local/bin /usr/sbin /usr/bin /sbin /bin /snap/bin

The following also are not working:

sudo find `echo $PATH | tr ':' ' ' | xargs` -lname /opt/openoffice4/program/soffice
sudo find { `echo $PATH | tr ':' ' ' | xargs` } -lname /opt/openoffice4/program/soffice
sudo find eval "echo $PATH | tr ':' ' ' | xargs" -lname /opt/openoffice4/program/soffice
echo $PATH | tr ':' ' ' | xargs | sudo find -lname /opt/openoffice4/program/soffice  # runs some long computation

How to pass starting point directories as calculable parameter to find? Does this possible?

1
  • 3
    You should not have ~ in your PATH. PATH is an environment variable for all applications. It is processed by C library functions like execvp and popen. Whereas ~ is POSIX shell syntax, for tilde expansion. The operating system doesn't understand ~, only the shell does.
    – Kaz
    Commented Apr 11 at 16:13

2 Answers 2

5

No need to use sudo, as your $PATH must contain directories that you can already access.

The ~ character is a built-in shortcut for the home directory for bash and other shells. But it isn't evaluated late enough for you to use it in your examples. (It also isn't evaluated in quoted expressions.)

When you are setting up your $PATH, instead of using something like PATH=~/bin:$PATH, consider using the more explicit alternative PATH="$HOME/bin:$PATH".

And for some code, here's an alternative solution that can handle spaces in $PATH directory names:

( IFS=:; for p in $PATH; do [ -d "$p" ] && find "$p" -maxdepth 1 -lname '/opt/openoffice4/program/soffice'; done )

More readably,

(
    IFS=:
    for p in $PATH
    do
        [ -d "$p" ] && find "$p" -maxdepth 1 -lname '/opt/openoffice4/program/soffice'
    done
)
5
  • 1
    ~ will be expanded (at the time of that assignment) if you run PATH=~/bin:$PATH. The OP must have run something like PATH='~/bin':$PATH. Commented Apr 11 at 14:53
  • I had export PATH="~/bin:$PATH" in ~/.bashrc. After fixing it to export PATH="$HOME/bin:$PATH" command find `echo $PATH | tr ':' ' '` -lname /opt/openoffice4/program/soffice started working without showing error. (IFS=:; find $PATH -lname /opt/openoffice4/program/soffice) is also working. Thanks! Commented Apr 11 at 18:05
  • @StéphaneChazelas with PATH=~/bin:$PATH also everything is fine, but I think it is better to use quotes around $PATH like export PATH=~/bin:"$PATH" for case if $PATH contains or will contain spaces. Commented Apr 11 at 18:11
  • 2
    @AntonSamokat, we quote expansions to prevent the splitting and globbing that happens otherwise in list contexts. Note that the splitting part is not on "spaces" but on characters of $IFS (which only happens to contain space by default). A scalar variable assignment is not one of those list contexts. PATH=~/bin:$PATH is a scalar variable assignment. And in bash, in export PATH=~/bin:$PATH the argument to export is also treated as a scalar assignment, so there won't be any split+glob. $PATH is already exported though so the export is superfluous. Quoting $PATH won't harm. ~ does Commented Apr 11 at 20:13
  • @StéphaneChazelas I've found related question about this: Expansion of a shell variable and effect of glob and split on it Commented Apr 13 at 17:33
4

Your $PATH does contain a ~/bin literally, so it will only find executables in a bin subdirectory of a directory called ~ literally in the current working directory, like after you run mkdir -p '~/bin'.

That ~/bin is bogus and should be removed.

If you run:

PATH=~/bin:$PATH

Where that ~ is not quoted, then you'll find that printf '%s\n' "$PATH" outputs:

/home/you/bin:~/bin:/home/samokat/.local/bin:/home/samokat/bin:...

That ~/bin will have been correctly expanded to the bin in your home directory, followed by the bogus ~/bin that was already there and the rest of your PATH.

But assuming you is actually samokat, that means that directory will occur twice in $PATH one of which redundant.

After you've fixed your $PATH, to find files in all directory components of $PATH, all you need in bash

(IFS=:; set -o noglob; find -H $PATH -lname /opt/openoffice4/program/soffice)

the $IFS special parameters determines how unquoted parameter expansions are being split. By default, $IFS contains the space character (as well as newline and tab) which explains why changing the :s to works, but it's much better to set IFS to the correct value. Also note the set -o noglob to disable the other side effect of leaving a parameter expansion unquoted.

Also note the -H without which find would not look into a directory that is actually a symlink to a directory.

Or if you can switch to zsh where $PATH is tied to the $path array (like in csh), then it's just:

find -H $path -lname /opt/openoffice4/program/soffice

In zsh, you can also use globs instead of find.

For instance:

print -rC1 -- $^path/*(N@e['[[ $REPLY:P = /opt/openoffice4/program/soffice ]]'])

would print raw on 1 Column the paths of the symlinks (@) inside each $path component whose realPath is /opt/openoffice4/program/soffice.

0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .