How to follow symlinks for which output? - bash

I have this handy function for showing symlinks from the which command.
function whichl() {
ls -la $(which -a $#)
}
It works great for things that are symlinked from the path, like python.
$ whichl python
-rwxr-xr-x 1 root wheel 66880 Mar 27 23:02 /usr/bin/python
lrwxr-xr-x 1 me admin 43 Dec 19 2017 /usr/local/bin/python -> /usr/local/Cellar/python/2.7.14/bin/python2
$ ls -la $(which -a python3)
lrwxr-xr-x 1 me admin 34 Jun 8 10:42 /usr/local/bin/python3 -> ../Cellar/python/3.6.5/bin/python3
It does not work so great when which doesn't find anything; the ls -la command runs against the current directory. This has confused me for the last time!
So, looking for two answers here.
Is there a better way to get which results to show symlinks?
I'm on OS X (if it wasn't obvious) and man which says my version is BSD December 13, 2006.
What is the best way to get my helper function to halt on when which returns no results? I've confirmed the return code in this case is 1, but a simple set -e doesn't change the behavior.

As I mentioned in the question, this doesn't work:
function whichl() {
set -e
ls -la $(which -a $#)
}
The explanation from the comments:
You call set -e in the current shell (where the function runs) but which is executed in a subshell (because of $(...)). You can store the value produced by $(which -a $#) in a variable and run ls -la only when it is not empty.
Following that suggestion, this is what I arrived at.
function whichl() {
RESULT=$(which -a $#)
if [ -z "$RESULT" ]; then
echo "No results for \"$#\""
return 1
fi
ls -la -- ${RESULT}
}
And it works perfectly from all I can test right now!
$ whichl python python3
-rwxr-xr-x 1 root wheel 66880 Mar 27 23:02 /usr/bin/python
lrwxr-xr-x 1 me admin 43 Dec 19 2017 /usr/local/bin/python -> /usr/local/Cellar/python/2.7.14/bin/python2
lrwxr-xr-x 1 me admin 34 Jun 8 10:42 /usr/local/bin/python3 -> ../Cellar/python/3.6.5/bin/python3
Notice it returns 3 results from 2 inputs, the system python and brew python.
$ whichl foo bar
No results for "foo bar"

Related

Behaviour of /dev/stdout in Cygwin

I have a script (executed with zsh 5.8; but this should not be relevant in this case) in a Cygwin environment, which takes as parameter the name of some output file and writes to this files via redirection in various places, like this:
outfile=$1
: >$outfile # Ensure that the file exists and is empty.
.... do some work
command_x >>$outfile
.... do more work
command_y >>$outfile
... and so on
I would like to modify the behviour of the script in that if no parameter is supplied, the output of the commands goes to standard output instead. I thought that it would be sufficient to modify the script in one line:
outfile=${1:-/dev/stdout}
But nothing is written to stdout. Investigating the case further, I found that instead a regular file named stdout had been created in the /dev directory. It seems that in the Cygwin environment, /dev/stdout does not represent the standard output of the process.
How would I achieve my goal under Cygwin?
UPDATE
As requested by #matzeri, here is a simple testcase:
echo x >/dev/stdout
Expected behaviour: Seeing x on stdout
Real behaviour: A regular file /dev/stdout has been created
on a standard windows installation the /dev/std* are a symlink to the /proc/self/fd/*
ls -l /dev/std*
lrwxrwxrwx 1 Marco Kein 15 Jun 19 2018 /dev/stderr -> /proc/self/fd/2
lrwxrwxrwx 1 Marco Kein 15 Jun 19 2018 /dev/stdin -> /proc/self/fd/0
lrwxrwxrwx 1 Marco Kein 15 Jun 19 2018 /dev/stdout -> /proc/self/fd/1
if for any reason that is not anymore true they can be recreated
by /etc/postinstall/bash.sh.done script
$ grep self /etc/postinstall/bash.sh.done
/bin/test -h /dev/stdin || ln -sf /proc/self/fd/0 /dev/stdin || result=1
/bin/test -h /dev/stdout || ln -sf /proc/self/fd/1 /dev/stdout || result=1
/bin/test -h /dev/stderr || ln -sf /proc/self/fd/2 /dev/stderr || result=1
/bin/test -h /dev/fd || ln -sf /proc/self/fd /dev/fd || result=1
In that condition the command
$ echo x > /dev/stdout
x
produces the expected output on both Bash and Zsh

Shell command "where" duplicate output

ls -al /usr/local/bin/shopify produces following output
lrwxr-xr-x 1 myuser admin 39 19 Jan 11:53 /usr/local/bin/shopify -> ../Cellar/shopify-cli/1.5.0/bin/shopify
Why is the output of where shopify duplicate?
as user1934428 commented, the path was listed twice in the .zshrc.
To avoid this in the future I put typeset -aU path after the last export statetement

Some relative paths on macOS 10.15 Catalina beta (19A471t) do not work

There's an odd behaviour when using relative paths. For example:
$ cd /Users
$ ls -l ../bin
ls: ../bin: No such file or directory
$ ls -l /bin
-r-xr-xr-x 1 root wheel 623344 31 May 08:33 bash
-rwxr-xr-x 1 root wheel 36768 31 May 08:33 cat
...
But the following works fine:
$ cd /dev
$ ls -l ../bin
-r-xr-xr-x 1 root wheel 623344 31 May 08:33 bash
-rwxr-xr-x 1 root wheel 36768 31 May 08:33 cat
...
Some other directories do not return the No such file or directory message, but they act as if there was nothing there. For example:
$ cd /Users
$ ls -l ../dev
$
returns nothing, and back to the prompt. The following, however, works fine:
$ cd /bin
$ ls -l ../dev
crw------- 1 root wheel 19, 1 11 Jun 16:54 afsc_type5
crw------- 1 root wheel 10, 0 11 Jun 16:54 auditpipe
crw-r--r-- 1 root wheel 9, 3 11 Jun 16:54 auditsessions
...
I could not find anything on the release notes. The WWDC2019 session 710 (What's New in Apple File Systems) also does not mention anything.
I think it might be related to the new separation of directories into a read-only and a read-write volumes. But still, it should work.
I found this to be specially problematic when using npm link, which links to /usr/local/lib/node_modules/... but expressed as a relative path from the destination package. After linking I have to manually change the link from relative to absolute. An ugly hack that may have some unforeseen consequences.
Anybody any clues?
The issue has been resolved with in Catalina beta 4.

mkdir doesn't do path expansion

So I have folder aa
$ mkdir aa
and path expansion for ls command works like this:
$ ls -la a*
total 0
drwxr-xr-x 1 a a 0 Mar 29 08:41 ./
drwxr-xr-x 1 a a 0 Dec 31 1979 ../
$ ls -la a?
total 0
drwxr-xr-x 1 a a 0 Mar 29 08:41 ./
drwxr-xr-x 1 a a 0 Dec 31 1979 ../
But "the same" for mkdir shows an error:
$ mkdir a*/bb
mkdir: cannot create directory 'a*/bb': No such file or directory
$ mkdir a?/bb
mkdir: cannot create directory 'a?/bb': No such file or directory
Where can I read why this difference in behavior happens and is there simple trick to let mkdir be "smarter" for behavior like in ls?
This does not work, since wildcard expansion is done before the argument is passed to mkdir. bash tries to expand a*/bb, doesn't find a match and tells you so. mkdir is not even invoked here. You can also try e.g.
echo a*/bb
or as you did before
ls -la a*/bb
Both commands will give you the same error message.
Now I realize how stupid that question was. Probably I wanted something like this for expansion to work:
mkdir "$(ls -d a?)"/bb
Try:
mkdir -p a*/aa
mkdir -p a?/aa

Print out the alias'd command when using the alias

I am trying to get alias's setup so that they print out the command, then run the command.
Ex:
> alias ls='ls -alh'
> ls
Running "ls -alh"
total 1.8G
drwxr-x--- 36 root root 4.0K Apr 23 09:44 ./
drwxr-xr-x 28 root root 4.0K Mar 6 17:24 ../
Is this possible? I was thinking of using a wrapper function, but I am unsure as to how one would acomplish this.
Thanks!
Just add an echo command in your alias before the actual command:
alias ls='echo "Running ls -alh"; ls -alh'
alias ls='echo "Running ls -alh" && ls -alh'
This runs two commands one after the other. The first command is echo "Running ls -alh", the && checks the return value of the echo command, if that's 0, then the command ls -alh is run. However, if for some reason there is a problem with the echo command and its return value is not 0 then the ls command won't be run.
The && command can come in very handy when writing scripts to run one command only when another is successful.

Resources