Categories
Code Linux

project change directory

I generally keep my development projects under a common directory like ~/dev/ or ~/projects/. Here is a simple bash shell script function which I use to quickly jump into and between projects on the command line. It includes auto completion, which is a pretty important part of its usefulness. You can put in your ~/.bashrc file and adapt the PROJECTS_PATH variable to suit your own needs.

PROJECTS_PATH=~/dev
pcd() {
    if [ "$1" = .. ]; then
        local reporoot=$(git rev-parse --show-toplevel 2>/dev/null)
        [ -d "$reporoot" ] && cd "$reporoot" || cd ..
    else
        cd "$PROJECTS_PATH"
        [ -d "$1" ] && cd "$1"
        [ -d "$2" ] && cd "$2"
    fi
}
_pcdcomp() {
    local IFS=$'\n'
    if [ "$COMP_CWORD" -eq 1 ]; then
        COMPREPLY=($(cd "$PROJECTS_PATH" && compgen -d "${COMP_WORDS[1]}"))
    elif [ "$COMP_CWORD" -eq 2 ]; then
        COMPREPLY=($(cd "$PROJECTS_PATH"/"${COMP_WORDS[1]}" 2>/dev/null && compgen -d "${COMP_WORDS[2]}"))
    else
        COMPREPLY=()
    fi
}
complete -o filenames -F _pcdcomp pcd

After loading the code, type pcd and hit TAB to see completion of all sub directories. Hit ENTER to jump to a project. It will also complete one level into a single project as second argument. Lastly pcd .. will jump up to the project root directory, if inside a git repository.

Screencast which shows how the pcd command works.
Screencast which shows how the pcd command works.
Categories
Code Linux

Query installed packages

Sometimes I just want to quickly find an installed package and show its version. Maybe I don’t even care if it’s a deb, snap or rpm package. Maybe I’d just like know if it is installed at all. And I’d like to search for it using words.

Here is a shell function qi (query installed) that can be added to ~/.bashrc or similar, which will list all installed deb, snap and/or rpm packages with version in a friendly format. You can easily narrow down the results by supplying words to filter. It mostly uses awk(1) to accomplish the task.

function qi() {
  (
      type dpkg-query &>/dev/null && \
        dpkg-query -W -f '${db:Status-Abbrev} ${Package} ${Version}\n'
      type snap &>/dev/null && \
        snap list|sed -e '/^Name/d' -e 's/^/snap /'
      type rpm &>/dev/null && \
        rpm -qa --qf 'rpm %{NAME} %{VERSION}-%{RELEASE}\n'
  ) | awk -v argline="$*" \
      'BEGIN { split(argline, fwords, / +/) }
       function include(package) {
         for (i in fwords) {
           if (index(package, tolower(fwords[i])) == 0) {
             return 0
           }
         }
         return 1
       }
       /^(ii|rpm)/ && include($2 $3) {
         printf("%-30s %s\n", $2, $3)
       }
       /^snap/ && include($2 $3 "[snap]") {
         printf("%-30s %-20s [snap]\n", $2, $3)
       }
      '
}

Update 17.12.2022: this version works seamlessly across Redhat and Debian based distros (also those not using snap).

The function consists of two main parts:

  1. A subshell which lists all installed packages in a particular format. It tests for available package managers and queries with those that are available. Its output stream is piped directly to an awk program.
  2. The awk program which does the filtering (highlighted in dark blue).

List all installed packages

$ qi
accountsservice                0.6.55-0ubuntu12~20.04.5
acl                            2.2.53-6
acpi-support                   0.143
acpid                          1:2.0.32-1ubuntu1
adduser                        3.118ubuntu2
adwaita-icon-theme             3.36.1-2ubuntu0.20.04.2
aisleriot                      1:3.22.9-1
alsa-base                      1.0.25+dfsg-0ubuntu5
alsa-topology-conf             1.2.2-1
alsa-ucm-conf                  1.2.2-1ubuntu0.13
[... 1945 more lines not shown here]

List packages matching words

$ qi chrom
chrome-gnome-shell             10.1-5
libchromaprint1                1.4.3-3build1
chromium                       107.0.5304.121       [snap]

Snap packages can be distinguished from debs by the [snap] marker in the third column.

$ qi image linux
linux-image-5.14.0-1054-oem    5.14.0-1054.61
linux-image-5.15.0-53-generic  5.15.0-53.59~20.04.1
linux-image-generic-hwe-20.04  5.15.0.53.59~20.04.21
linux-image-oem-20.04d         5.14.0.1054.52

Use multiple words to narrow down, and the ordering does not matter. All supplied words must be substrings of the package name and version for a match to occur.

More examples

List only snap packages:

$ qi \[snap\]
bare                           1.0                  [snap]
chromium                       107.0.5304.121       [snap]
core                           16-2.57.4            [snap]
core18                         20221103             [snap]
core20                         20221027             [snap]
cups                           2.4.2-4              [snap]
docker                         20.10.17             [snap]
[...]

Search within -dev packages:

$ qi -dev ssl
libssl-dev                     1.1.1f-1ubuntu2.16
$ qi gtk dev
libgtk-3-dev                   3.24.20-0ubuntu1.1

If you want grep filtering, just use grep:

$ qi|grep -E 'ssl|tls'
libcurl3-gnutls                7.68.0-1ubuntu2.14
libgnutls30                    3.6.13-2ubuntu1.7
libio-socket-ssl-perl          2.067-1
libneon27-gnutls               0.30.2-4
libnet-smtp-ssl-perl           1.04-1
libnet-ssleay-perl             1.88-2ubuntu1
libssl-dev                     1.1.1f-1ubuntu2.16
libssl1.1                      1.1.1f-1ubuntu2.16
openssl                        1.1.1f-1ubuntu2.16
[...]

Other package formats

It should be a simple matter to add support for other package formats. Just add to the commands which supplies the package lists in the first sub shell, keeping in mind the common output format. Then possibly add prefix patterns for the awk program to recognize and match on lines from other package managers.

Categories
Code

Navigating Maven projects on the command line

Or how to avoid..

~/dev/myproject/src/main/java/com/example/foo $ cd ..
~/dev/myproject/src/main/java/com/example $ cd ..
~/dev/myproject/src/main/java/com $ cd ..
~/dev/myproject/src/main/java $ cd ../../..
~/dev/myproject $

This following Bash shell function, which is called pom.. (yes with two dots in the name), will allow you to navigate up to the closest ancestor directory containing a pom.xml file (closest module) with one command. Put it in your ~/.bashrc:

function pom..() {
    local start_dir="$(pwd)" prev_dir= rel_dir="$1"
    while [ "$prev_dir" != "$(pwd)" ]; do
        prev_dir="$(pwd)"
        cd ..
        if [ -f pom.xml ]; then
            if [ -d "$rel_dir" ]; then
                cd "$rel_dir"
            elif [ "$rel_dir" ]; then
                echo >&2 "Directory not found relative to pom.xml: $(pwd)/$rel_dir"
                cd "$start_dir"
                return 1
            fi
            pwd|sed "s#^$HOME#~#"
            return 0
        fi
    done
    echo >&2 "No pom.xml found in ancestor directories."
    cd "$start_dir"
    return 1
}

So you don’t have to waste any more time typing “cd ..” multiple times when navigating upwards to a Maven module root on the command line. Just type pom.. once.

~/dev/myproject/src/main/java/com/example/foo $ pom..
~/dev/myproject
~/dev/myproject $ 

It also accepts an optional argument, which is a desired directory relative to the nearest POM:

~/dev/myproject/src/main/java/com/example/foo $ pom.. target
~/dev/myproject/target
~/dev/myproject/target $ pom.. src/test
~/dev/myproject/src/test
~/dev/myproject/src/test $

The strategy will work for any style of hierarchically organized source code project where a typical marker file or directory exists at certain source code roots. Just be creative and modify the code.