Continuing our series on useful Bash command line tips and tricks, in today’s article, we will explore grepping only what you need, and start of with a primer on pwd
and how to discover the path a script was started from.
In this tutorial you will learn:
- Useful Bash command line tips, tricks and methods
- How to interact with the Bash command line in an advanced manner
- How to sharpen your Bash skills overall and become a more proficient Bash user
Software requirements and conventions used
Category | Requirements, Conventions or Software Version Used |
---|---|
System | Linux Distribution-independent |
Software | Bash command line, Linux based system |
Other | Any utility which is not included in the Bash shell by default can be installed using sudo apt-get install utility-name (or yum install for RedHat based systems) |
Conventions | # – requires linux-commands to be executed with root privileges either directly as a root user or by use of sudo command$ – requires linux-commands to be executed as a regular non-privileged user |
Example 1: Path working directory? Or not?
We may be used to calling pwd
at the command line to tell us the name of our current directory:
$ pwd /home/roel/workspace
But if we want to obtain the directory from which a script was started, does using this variable from a script also work the same way?
$ cat test_pwd.sh
#!/bin/bash
MY_PATH1="${PWD}"
echo "${MY_PATH1}"
MY_PATH2="$(pwd)"
echo "${MY_PATH2}"
We will use two ways to retrieve the path: we look (for the first MY_PATH1
assignment) at the PWD
(OS level) automatically-preset variable, and in the second case (for the second MY_PATH2
assignment) we execute pwd
in a subshell ($(...)
) and assign it to MY_PATH2
. Let’s execute the same:
$ ./test_pwd.sh /home/roel/workspace /home/roel/workspace
Looks all good, right? Yes, perhaps, but once we change into a different directory, and call the script by using it’s full or relative path name, the PWD
variable (or the pwd
command) inside the script will return the directory we were in when the script was started. Let’s verify this:
$ mkdir test $ cd test $ ../test_pwd.sh /home/roel/workspace/test /home/roel/workspace/test
What we can see here is that pwd
(or the $PWD
variable) will always return the actual current path.
This may be an issue: we may want to start a subscript, or access a file, from within the same directory (or a subdirectory thereof) as the one where the script was in. This is often the case for more complex Bash scripts which call multiple subscripts and/or data and configuration files.
So what is a uniform way to obtain the path from which the script was started?
One may consider using the dirname "\$0"
command:
$ cat test2.sh
#!/bin/bash
echo "\$0"
dirname "\$0"
Dirname will yield the – relative – directory name of any option passed to it, and in this case that is \$0
– a special variable which is set when a script starts to the name of the script as it was started. Let’s test it:
$ ./test2.sh ./test2.sh . $ cd test $ ../test2.sh ../test2.sh ..
Better, I suppose, and it may help a little (to get an idea mainly), but it still does not yield us with a fully qualified pathname. For this, we can use a double subshell wrapper around dirname
in combination with an actual full qualified path obtained from a pwd
execution:
$ cat make_it_work.sh
#!/bin/bash
MY_PATH="$(cd "$(dirname "\$0")" && pwd)"
echo "${MY_PATH}"
Let’s see if it works:
$ ./make_it_work.sh /home/roel/workspace
So far so good, but does it work when you run it from a subdirectory?
$ cd test && pwd /home/roel/workspace/test $ ../make_it_work.sh /home/roel/workspace $ /home/roel/workspace/make_it_work.sh /home/roel/workspace
Yes!
We first change into test
and verify we are in the test
directory, if that change directory (cd
) command was successful (as instructed by &&
).
In both cases – whether it is called in a relative manner (../
), or with a fully qualified path name (/home/roel/workspace/
), the result is that we see the directory from which the script was started (/home/roel/workspace/
) and not the actual current working directory, or pwd (/home/roel/workspace/test
).
In summary, you can use the MY_PATH="$(cd "$(dirname "\$0")" && pwd)"
one-liner script to obtain the correct, fully qualified, directory name from within your scripts. After this, using a relative addition will work nicely. For example, you can use ${MY_PATH}/include/mysubscript.sh
and even ${MY_PATH}/../one_dir_up_file.txt
etc.
Example 2: Grepping only what you need
Have you ever used grep
? Grep is a versatile Bash command line utility which lets you select text from a file easily. Most of the time it is used as follows:
$ cat test line 1 my line line 2 your lines line 3 our line $ grep 'line 2' test line 2 your lines
We have a input file with 3 lines, and we look for a specific test (line 2
) in that file. But how about if you only want the lines which have the multiple of line
(i.e. lines
) mentioned in them? And, how about if you only wanted to have just the word before it including the lines
bit, but not the actual line x
output?
In that case, we can grep for only what we need using the -o
(‘only’) option to grep:
$ grep -o 'lines' test lines $ grep -o '\w\+ lines' test your lines
Bingo! We first grepped for only the word ‘lines’, which was found. We then prefixed that with a space, and a regular expression which states in pseudo-text: find a word boundary (\w
), at least once and as many times as applicable (\+
) followed by a space. The result is that your
(which has two word boundaries; a start and an end) is included. line 2
is not included. Whilst it has word boundaries, there is no suffix of lines
to that one.
If you would like to learn more about regular expressions in Bash, please see Bash Regexps for Beginners with Examples, Advanced Bash Regex with Examples and you may also like the semi-related Python Regular Expressions with Examples.
Conclusion
In this article, we explored pwd
and looked at how to use pwd
from within a script in combination with dirname
to retrieve the fully qualified directory path from which the script was started, in a way which will always work. We also looked at grepping only what we need using the -o
option and, in this case, regular expression to match word boundaries.
Leave us a comment with your best Bash command line tips and tricks!