bashshellscriptingdirectory-structurealiases

Creating an alias in bash that calls a script and an additional command


I was toying with the idea of creating an alias that would allow me to list the contents of a target subdirectory, without changing to that directory.

I have successfully been able to create both an alias and a script to change directory and display contents. I call the script leap and it is simply:

#!/bin/bash

PATH=/bin:/usr/bin:.

# script to change to a directory, then display current contents
cd $1 && ls -l -a

The alias I use to trigger leap is defined: alias lp='. ~/scripts/leap'

My hope was I could simply create an alias named pk (pk is for peek) and concatenate my leap script and a standard bash command using &&. I could not be more wrong.

Current Directory

For reference, here is the contents of my current home directory (my user id has been replaced with $$$$):

drwxr-xr-x 3 $$$$$$    46374   23 Aug 30 11:40 Fall_2019
drwxr-xr-x 5 $$$$$$    46374   66 Aug 28 09:01 PAST_COURSES
drwxr-xr-x 3 $$$$$$    46374   22 Aug 30 12:03 repos
drwxr-xr-x 3 $$$$$$ students  117 Aug 31 09:06 scripts

The Attempt(s)

Using alias pk='lp $1 && cd ..' Entering [$$$$@host ~]$ pk PAST_COURSES results in:

-rw-------    1 $$$$$$ students  1766 Feb 28  2018 ~
drwx------   10 $$$$$$ students  4096 Aug 31 09:06 .
drwx--x--x 1232 root   root     28672 Aug 30 16:03 ..
-rw-------    1 $$$$$$ students 11368 Aug 30 12:20 .bash_history
-rw-------    1 $$$$$$ students    18 Aug 21  2017 .bash_logout
-rw-------    1 $$$$$$ students   180 Mar  8  2018 .bash_profile
-rw-------    1 $$$$$$ students   526 Aug 30 11:19 .bashrc
-rw-------    1 $$$$$$ students   266 Aug 21  2017 .cshrc
drwxr-xr-x    3 $$$$$$     46374    23 Aug 30 11:40 Fall_2019
drwxr-xr-x    8 $$$$$$ students   155 Aug 30 12:14 .git
-rw-r--r--    1 $$$$$$ students    87 Apr 11  2018 .gitconfig
-rw-r--r--    1 $$$$$$ students 12288 Jan 29  2018 .hello.swp
-rw-------    1 $$$$$$ students   172 Aug 21  2017 .kshrc
-rw-------    1 $$$$$$ students   189 Mar 13  2018 .lesshst
-rw-r--r--    1 $$$$$$ students 20480 Jan 29  2018 .ls.swn
drwxr-xr-x    5 $$$$$$ 46374    66 Aug 28 09:01 PAST_COURSES
drwxr-----    3 $$$$$$ 46374    18 Aug 30 12:16 .pki
drwxr-xr-x    3 $$$$$$ 46374    22 Aug 30 12:03 repos
drwxr-xr-x    3 $$$$$$ students   117 Aug 31 09:06 scripts
drwx------    3 $$$$$$ students   103 Aug 30 11:12 .ssh
-rw-------    1 $$$$$$ students 12288 Sep  6  2017 .swp
drwxr-xr-x    2 $$$$$$ 46374    23 Aug 31 09:06 .vim
-rw-r--r--    1 $$$$$$ students  8129 Aug 31 09:06 .viminfo
-rw-r--r--    1 $$$$$$ students   142 Feb 14  2018 .vimrc
[$$$$@host home]$ 

As you can see, this displays the current directory ( ~ ) rather than switching to PAST_COURSES and displaying it. Additionally, the alias jumps up one directory above the current directory ( ~ ) rather than returning to it from PAST_COURSES.

Incidentally, I also get this exact result when I try using the following aliases:

pk='. ~/scripts/leap $1 && cd ..' (using the script for leap rather than the alias)

pk='cd $1 && ls -l -a && cd ..' (using the exact code inside leap )

Findings

In my tinkering I have noticed a few things:

At this point, I'd like a little help - not just in a script or alias that will do the job. An explanation that explains this behavior that I'm seeing would be marvelous.


Solution

  • Don't use . (the source builtin)

    alias pk='. ~/scripts/leap $1 && cd ..' (using the script for leap rather than the alias)
    

    is not equivalent to

    alias pk='cd $1 && ls -l -a && cd ..'
    

    In the first one, the . builtin (also known as source) is used, while in the second it is not. . doesn't just execute a command, it executes it in the current shell context. From the documentation:

    . (a period)

    . filename [arguments]

    Read and execute commands from the filename argument in the current shell context.

    That means anything the script does effects your current shell context. If the script changes directories, so does your current context.

    If, on the other hand, the first version omitted the . like this:

    alias pk='~/scripts/leap $1 && cd ..'
    

    then the contents of the leap script would run in it's own bash context, but your current context would move up one directory (since the cd .. isn't inside the leap script).

    Additional Recommendation on Functions vs Aliases

    You could implement pk using a function like this:

    pk() {
      pushd $1
    
      if [[ $? == 0 ]]; then
        # Successfully changed directories.
    
        # Run command
        $2
    
        # Return from the pre-pushd directory.
        popd
      fi
    
    }
    

    From the Bash Manual | 6.6 Aliases:

    For almost every purpose, shell functions are preferred over aliases.

    Example Alias

    alias foo="echo bar"
    

    Equivalent Function

    foo() {
        echo bar
    }