Case 2: When the argument is a filepath
*/* )
This is clause #2 of our case statement, with */*
as the pattern that the case statement will match against.
One difference I notice is that this is not the same as a "catch-all" default case, because the */* )
pattern here doesn't exactly match the * )
pattern in the example code we read earlier.
Instead, it looks like this pattern searches for a forward-slash, surrounded on either side with zero or more characters. The one thing I can think of which would match that pattern is a file path. Let's check that with an experiment.
Experiment: matching the */*
pattern
Let's make a Bash script which looks like this:
#!/usr/bin/env bash
for arg; do
case "$arg" in
*/* )
echo "match: $arg";
;;
* )
echo "not a match: $arg";
;;
esac
done
When I run the above with a few different arguments, I get the following results:
$ ./foo 1 2 3 a/b /a b/ / ''
not a match: 1
not a match: 2
not a match: 3
match: a/b
match: /a
match: b/
match: /
not a match:
$
I sent my ./foo
script the following arguments:
- 3 arguments with no forward slashes at all, just 3 random numbers ("1", 2", and "3").
- 4 arguments containing a forward slash in various positions
- an empty string, ''
When I run the script:
- the first group of 3 patterns don't match
- the second group of 4 patterns do match
- the final empty string does not match
So yes, it appears to be looking for strings which match the /
symbol with zero or more characters of text on either side.
But just because it looks like a valid filepath, doesn't mean it is one. So how do we know it's a file?
We'll answer that question next.
Detecting Filepaths
if [ -f "$arg" ]; then
...
fi
Running man test
and searching for the -f
string reveals the following:
$ man test | ag -- -f
-f file True if file exists and is a regular file.
$
It says that [ -f "$file" ]
returns true "if file
exists and is a regular file". In other words, our case statement matches if the arg could be a filepath, and we then verify that it actually is a filepath by using [ -f "$arg" ]
.
To test whether the -f
flag behaves the way I think it does, we can update our foo
script from earlier to look like the following:
#!/usr/bin/env bash
for arg; do
case "$arg" in
*/* )
echo "could be a filepath: $arg";
if [ -f "$arg" ]; then
echo "is definitely a filepath";
else
echo "turns out, not a filepath";
fi
;;
* )
echo "not a match: $arg";
;;
esac
echo "-----"
done
Then I create an empty file named bar
in my current directory:
$ touch bar
$
I do this so that I have a file in my directory that will return true for the test [ -f "./bar" ]
.
Lastly, I run the foo
script with a few arguments, and I see the following:
$ ./foo 1 2 3 a/b /a b/ / '' ./bar foo
not a match: 1
-----
not a match: 2
-----
not a match: 3
-----
could be a filepath: a/b
turns out, not a filepath
-----
could be a filepath: /a
turns out, not a filepath
-----
could be a filepath: b/
turns out, not a filepath
-----
could be a filepath: /
turns out, not a filepath
-----
not a match:
-----
could be a filepath: ./bar
is definitely a filepath
-----
not a match: foo
-----
$
As expected, the arguments which are known to not match a file in my current directory (i.e. "a/b", "/b", "a/", and "/") result in the output "turns out, is not a filepath". My "./bar" argument, which is known to match a file, results in the output "is definitely a filepath".
Only matching certain filepaths
But wait, the last argument "foo" also matches a filepath, i.e. the ./foo
script we're running!
The case statement doesn't recognize it as a match, because it doesn't have a "/" in its argument. But it is a file. Since this case statement closely matches the one in RBENV's shim, does that imply the RBENV shim is also skipping some filepaths? And if so, is that on purpose, or should it in fact be treating them as matches?
The short answer is that yes, we are skipping files which don't have a /
in them. And that is intentional, because we can assume that files which don't have a /
in them are in our current directory.
I'm skipping ahead a bit, but the purpose of this part of the case
statement is to set an environment variable called RBENV_DIR
. This environment variable controls where RBENV searches for a file called .ruby-version
, which tells it which version of Ruby to use. We only want to set that environment variable from within the shim if the Ruby script we're running lives somewhere other than in our current directory.
So for example, let's assume we're running a script which is located inside our current directory, i.e. we're inside ~/foo/
and we want to run ruby ~/foo/buzz.rb
. In that case, we assume that the .ruby-version
file also lives in the current directory. So we don't need to look for .ruby-version
from inside the shim, because the rbenv
command (which we'll look at later) will handle that.
On the other hand, if we're currently in ~/foo/
, but we're running ruby ~/bar/baz.rb
, then we want RBENV to use the .ruby-version
file inside ~/bar/
, not the .ruby-version
file inside ~/foo/
.
Wrapping Up
Don't worry if you weren't able to put this together yourself, just based on the code we've read up to this point. I wasn't able to either, and it wasn't until I asked this question in the RBENV Github repo that this all came together for me.
Additionally, in some cases I've thought of questions which I haven't been able to answer in the moment. One important habit that I've had to develop while working on this project is capturing my open questions in a document, and setting them aside until I've read more code and have more knowledge of all the moving parts. Oftentimes, when I come back to those questions after some time, I'm able to answer them myself with the knowledge I've acquired since then.
Let's now move on to a topic that we touched on briefly above- initializing the RBENV_DIR
variable.