Showing posts with label linux. Show all posts
Showing posts with label linux. Show all posts

Tuesday, March 31, 2026

List outdated packages in Python's pipx without upgrading

pipx does not currently provide a built in method of listing outdated packages. A short bash function shared in an open GitHub issue provides a workaround.

Introduction

I've been using pipx to manage my Python application for roughly three years. As the documentation states, "pipx installs and runs end-user Python apllications(and their respective dependencies) in isolated environments". I'm able to keep my pip list relatively clean as well as isolate my installed applications in separate virtual environments(venvs). Being able to minimize dependency conflicts has alleviated some of the headaches that previously came with installing all my applications and libraries via pip. Since I'm also using pyenv, I can also install them using different Python versions, if necessary.

I do, however, have one nit to pick with pipx. Currently, there is no way to check for available upgrades without actually performing the upgrades. That is, by running pipx upgrade-all and, potentially, upgrading all installed applications. This is time consuming as pipx needs to go through my entire list of installed applications and does not allow me to review upgradeable packages individually. pip has this functionality built in with the command pip list --outdated. There is also a separate package for managing pip package installations called pip-check which I use often and have installed via pipx.

Fortunately, a few years ago, I came across this issue: Feature request: Option to list available upgrades without performing them. While the issue remains open after almost seven years, there is some interesting discussion there as well a couple posts containing workable solutions. I've chosen to implement @StaticPH's comment as my preferred solution.

Solution


pipx-outdated() {
    # See: https://github.com/pypa/pipx/issues/149#issuecomment-684042303
    echo "OUTDATED PACKAGES:"
    while read -sr pyPkgName pyPkgVersion; do
        pyPkgURL="https://pypi.org/pypi/${pyPkgName}/json"
        pypi_latest="$(curl -sS "${pyPkgURL}" | jq --raw-output '.info.version')"
        [ "$pyPkgVersion" != "$pypi_latest" ] && printf "%s\n\tCurrent: \
        %s\tLatest: %s\n" "$pyPkgName" "$pyPkgVersion" "$pypi_latest"
    done <<( pipx list | grep -o 'package.*,' | tr -d ',' | cut -d ' ' -f 2- )
}

The pipx-outdated function greps through the output of pipx list to get the package name and its currently installed version. Next, using curl, it constructs the PyPi package URL and extracts the latest version from the available json file and compares the installed version to the latest. If there is a difference between the two, a list of outdated packages is print out to the console. While this function may ignore dev or alpha release, it is adequate for my use.

At this point, I can selectively upgrade the packages that I am interested or pass pipx upgrade only the packages I choose to upgrade at this time. Adding this to my .bash_aliases file allows me to run this whenever its needed.

Here's an example of the output of the pipx-outdated function showing two packages that have updates available.

  
    ~$ pipx-outdated
    OUTDATED PACKAGES:
    glances
        Current: 4.5.2  Latest: 4.5.3
    hike
        Current: 1.3.0  Latest: 1.4.0
  

I could choose either to upgrade a single package pipx upgrade ruff or I could upgrade both simultaneously with the command pipx upgrade glances ruff.

While it may be convenient to have this feature eventually integrated into the pipx package proper, I'm grateful to the folks who create and share solutions to paper-cuts as well as the developers who maintain and support the pipx package.

Resources

Saturday, February 28, 2026

PostgreSQL: collation version mismatch

Resolving "collation version mismatch" warnings after a PostgreSQL upgrade

Introduction

This article discusses the appearance and resolution of "collation version mismatch" warning messages appearing in the application logs after upgrading a PostgreSQL database.

Here is an abridged example of the warning messages seen in the logs:


WARNING: database "postgres" has a collation version mismatch
DETAIL: The database was created using collation version 2.36,
but the operating system provides version 2.41.
HINT: Rebuild all objects in this database that use the default
collation and run ALTER DATABASE postgres REFRESH COLLATION VERSION,
or build PostgreSQL with the right library version.

This is a great example of application developers providing clear and concise log messages for the end user.

According to the PostgreSQL documentation:

A change in collation definitions can lead to corrupt indexes and other problems because the database system relies on stored objects having a certain sort order. Generally, this should be avoided, but it can happen in legitimate circumstances, such as when upgrading the operating system to a new major version or when using pg_upgrade to upgrade to server binaries linked with a newer version of ICU. When this happens, all objects depending on the collation should be rebuilt, for example, using REINDEX. When that is done, the collation version can be refreshed using the command ALTER COLLATION ... REFRESH VERSION. This will update the system catalog to record the current collation version and will make the warning go away. Note that this does not actually check whether all affected objects have been rebuilt correctly.

Note: For this particular use case, I am running Django(5.2.x) web applications with a PostgreSQL(15.x) database back end within Docker(29.2.1) containers on a Debian testing(trixie) Linux distribution.

Instructions

  1. Update docker-compose.yml with new version of the PostgreSQL database.
    • For example, replace the statement image: postgres:15.11 with image: postgres:15.17.
    • See the official PostgreSQL Docker image page for additional information.
  2. Rebuild the Docker environment: docker compose up --build
  3. Once the build is finished, the warning messages mentioned above will start appearing in the logs.

  4. Make a note of all the databases listed in the log messages that need to be rebuilt. In this case, we have four databases to update:

    • test_django-start
    • template1
    • postgres
    • django-start
  5. Log into the database container:

    sh docker exec -it django_start-db bash

  6. Connect to the first database.

    psql -d test_django-start -U django_admin

  7. Execute the commands appropriate to the connected database.

    ALTER DATABASE "test_django-start" REFRESH COLLATION VERSION;

    REINDEX DATABASE "test_django-start";

    \c template1

    ALTER DATABASE template1 REFRESH COLLATION VERSION;

    REINDEX DATABASE template1;

    \c postgres

    ALTER DATABASE postgres REFRESH COLLATION VERSION;

    REINDEX DATABASE postgres;

    \c django-start

    ALTER DATABASE "django-start" REFRESH COLLATION VERSION;

    REINDEX DATABASE "django-start";

    \q # quit psql

    exit # exit container

  8. Restart the database container.

    docker stop django_start-db docker start django_start-db

Further Reading

Saturday, January 31, 2026

Managing Ruby installations using rbenv and ruby-build

Installing rbenv and ruby-build on Debian-based systems to manage Ruby versions

"rbenv is a version manager tool for the Ruby programming language on Unix-like systems. It is useful for switching between multiple Ruby versions on the same machine and for ensuring that each project you are working on always runs on the correct Ruby version." -- rbenv's README

Introduction

In this article, I will provide steps to install and configure rbenv and ruby-build on a Debian-based Linux system.

Similar to other programming languages(e.g. Python, Perl, and Rust, etc.), I prefer to keep the version of Ruby I use separate from the version installed by my system. This includes any plugins, or packages(in Ruby's case gems). Aside from giving me more granular control, it also helps prevent me from potentially breaking my system by installing an incompatible version of the language.

I've chosen to use rbenv and ruby-build to provide me with a user-controlled environment in which to manage my Ruby installations. There are several other tools(e.g. rvm and asdf that provide similar functionality. My reasoning for selecting rbenv as my tool of choice is simple. I've been using pyenv for several years to manage my Python installations. Pyenv is a direct fork of rbenv, their developers contribute upstream to rbenv and has a familiar command set. Among other features, rbenv provides support for specifying application-specific Ruby versions(via the rbenv local command).

See rbenv's README for additional details on installation and functionality.

For additional information on rbenv, managing gems, or installing Ruby on Rails, take a look a the links provided in the Further Reading section.

Installation

Installing System Prerequisites

Before installing rbenv, we need to ensure that the system has some necessary dependencies installed. From the command line, enter the following commands:


sudo apt update
sudo apt install git curl libssl-dev \
    libreadline-dev zlib1g-dev autoconf \
    bison build-essential libyaml-dev \
    libreadline-dev libncurses5-dev \
    libffi-dev libgdbm-dev libsqlite3-dev

Depending on your system, you may already have some of these packages installed, or you may need to install additional dependencies. The output from sudo apt install should provide additional guidance. system.

Installing rbenv

  1. Clone rbenv into ~/.rbenv.
    • git clone https://github.com/rbenv/rbenv.git ~/.rbenv
  2. Configure your shell to load rbenv when starting the terminal:
    • echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
    • echo 'eval"$(rbenv init - bash)"' >> ~/.bashrc
  3. Reload your shell configuration for changes to take effect. source ~/.bashrc

NOTE: If you are using a shell other than bash, replace ~/.bashrc in the above commands with the appropriate filename(e.g. ~/.zshrc or ~/.config/fish/config.fish)

Installing ruby-build

ruby-build will need to be installed to help compile Ruby binaries. Run the following commands to create a directory for the ruby-build plugin and then download it to the proper directory:


mkdir -p "$(rbenv root)"/plugins
git clone https://github.com/rbenv/ruby-build.git "$(rbenv root)"/plugins/ruby-build

Verifying installation

  1. Check installed version of rbenv

~$rbenv -v (or --version)
rbenv 1.3.2-16-gba96d7e

To check which version of Ruby is installed, use rbenv version(no dashes).

  1. Run rbenv-doctor

The rbenv-doctor script analyzes your system setup for common problems. Run this script to verify that the installation was successful.

curl -fsSL https://github.com/rbenv/rbenv-installer/raw/HEAD/bin/rbenv-doctor | bash

or

wget -q https://github.com/rbenv/rbenv-installer/raw/HEAD/bin/rbenv-doctor -O- | bash

Either of the commands should produce output similar to the following:


Checking for rbenv shims in PATH: OK
Checking `rbenv install' support: ~/.rbenv/plugins/ruby-build/bin/rbenv-install (ruby-build 20260121)
Counting installed Ruby versions: 1 versions
Auditing installed plugins: OK

For additional troubleshooting assistance, refer to the rbenv wiki

Installing Ruby

With rbenv installed, an updated list of the available Ruby versions can be viewed with the command:


rbenv install -l

#Output

3.2.10
3.3.10
3.4.8
4.0.1
jruby-10.0.2.0
mruby-3.4.0
picoruby-3.0.0
truffleruby-33.0.1
truffleruby+graalvm-33.0.1

Only latest stable releases for each Ruby implementation are shown. Use `rbenv install --list-all' to show all local versions.

As of this writing(20260128), the latest version of Ruby is 4.0.1.


    rbenv install 4.0.1 --verbose

This command should take roughly fifteen minutes to complete. Using the --verbose flag will produce output to the console so that the installation progress can be observed.

Set Default Ruby Version

Once Ruby is installed, set the default version using the global option:


rbenv global 4.0.1

Next, verify that Ruby was properly installed by checking its version number:


ruby --version

#Output

ruby 4.0.1 (2026-01-13 revision e04267a14b) +PRISM [x86_64-linux]

To install and use a different version of Ruby, run the rbenv commands with a different version number, such as rbenv install 3.4.8 and rbenv global 3.4.8.

Removing unneeded Ruby versions

The rbenv uninstall command can be used to remove old versions of Ruby that are no longer needed.


rbenv uninstall 4.0.1

Updating rbenv and ruby-build

Updating rbenv


cd ~/.rbenv
git pull

Updating ruby-build


cd ~/.rbenv
git -C plugins/ruby-build pull

Further Reading

rbenv alternatives

Wednesday, May 14, 2025

Linux find Command Reference & Cookbook

Using the Linux find command with examples

Looking for a specific command?
Jump to the Cookbook section.


Introduction

Even though I use fzf - fuzzy finder and have integrated it into my shell, I continue to use the traditional find tool. The following document provides an overview of some of the details regarding the use of the Linux find command as well as a cookbook of practical examples demonstrating the use of the command.
The first portion of the document consists of details and explanations from the man pages for find as well as some summaries of the details regarding the usage of the find command.
The second part consists of practical examples of find usages to solve specific search & action requirements.

Name

find - search for files in a directory hierarchy

Description

The find command is used to locate files. find will search any set of directories you specify for files that match the supplied search criteria. find searches the directory tree rooted at each given starting-point by evaluating the given expression.

find can search for files, or directories, by:

  • name
  • owner
  • group
  • type
  • permissions
  • date

find can also be used to execute commands (e.g. grep, mv, rm, etc.) to act upon the query results.


Synopsis

The syntax structure for searching with find looks like this:

find [-H] [-L] [-P] [-D debugopts] [-Olevel] [starting-point...] [expression]

The command has, essentially, three sections which can be briefly summarized as:

find <options> <starting-point> <expression>
  • [options] - The -H, -L and -P options control the treatment of symbolic links. The -D and -O options control diagnostic output and query optimizations, respectively.
  • [starting-point] - List of directories where to search
  • [expression] - Expressions filter the search or perform actions on the files found.

All arguments to find are optional, below are the defaults for each query section:

  • [options] - defaults to . (the current working directory)
  • [starting-point] - defaults to none (select all files)
  • [expressions] (known as the find action) - defaults to ‑print (display the names of found files to standard output).

Technically, the options and actions are all known as find primaries.


Options

The -H, -L and -P options control the treatment of symbolic links. Command-line arguments following these are taken to be names of files or directories to be examined, up to the first argument that begins with -, or the argument ( or !. That argument and any following arguments are taken to be the expression describing what is to be searched for. If no paths are given, the current directory is used. If no expression is given, the expression -print is used.

-H Do not follow symbolic links, except while processing command line arguments.
-L Follow symbolic links.
-P Never follow symbolic links.
-D debugopts Print diagnostic information. For a complete list of valid debug options, see the output of find -D help.

- exec       Show diagnostic information relating to -exec, -execdir, -ok and -okdir
- opt        Show diagnostic information relating to optimisation
- rates      Indicate how often each predicate succeeded
- search     Navigate the directory tree verbosely
- stat       Trace calls to stat(2) and lstat(2)
- time       Show diagnostic information relating to time-of-day and timestamp comparisons
- tree       Display the expression tree
- all        Set all of the debug flags (but help)
- help       Explain the various -D options  

Olevel Enables query optimization. The find program reorders tests to speed up execution while preserving the overall effect.

For example:

find -D exec -name test.txt -type f -execdir mv {} example.txt \;

Results:

DebugExec: launching process (argc=3): ‘mv’ ‘./test.txt’ ‘example.txt’
DebugExec: process (PID=25192) terminated with exit status: 0

As with all items outlined here, refer to man find for additional, specific details.


Expressions

The part of the command line after the list of starting points is the expression. This is a kind of query specification describing how we match files and what we do with the files that were matched. An expression is composed of a sequence of things:

  • Test expressions
    • Tests return a true or false value, usually on the basis of some property of a file we are considering. The -empty test for example is true only when the current file is empty.
  • Action expressions
    • Actions have side effects (such as printing something on the standard output) and return either true or false, usually based on whether or not they are successful. The -print action for example prints the name of the current file on the standard output.
  • Global options
    • Global options affect the operation of tests and actions specified on any part of the command line. Global options always return true. The -depth option for example makes find traverse the file system in a depth-first order.
  • Positional options
    • Positional options affect only tests or actions which follow them. Positional options always return true. The -regextype option for example is positional, specifying the regular expression dialect for regular expressions occurring later on the command line.
  • Operators
    • Operators join together the other items within the expression. They include for example -o (meaning logical OR) and -a (meaning logical AND). Where an operator is missing, -a is assumed.

When using multiple expressions without specifying any operator, the AND operator is implicitly used.

Example Directory Structure

For the next two sections(test expressions and action expressions), the examples will be using the directory structure below. If you want to follow along with the examples, re-create this on your local machine.

        .testdir        # (root/current working directory)  
        ├── example.txt  
        ├── image.jpg  
        ├── topdir1  
        │   ├── dir_a  
        │   │   └── image.jpg  
        │   ├── dir_b  
        │   │   ├── example.txt  
        │   │   └── image.jpg  
        │   ├── dir_c  
        │   └── image.jpg  
        ├── topdir2  
        │   ├── dir_a  
        │   │   └── image.jpg  
        │   ├── dir_b  
        │   ├── dir_c  
        │   │   └── image.jpg  
        │   ├── myfile.txt  
        │   └── myfile1.txt  
        └── topdir3  
            ├── dir_a  
            │   └── image.jpg  
            ├── dir_b  
            │   └── MyFile.txt  
            └── image.jpg  

Note: the folder.jpg & myfile1.txt should not be empty files. Use an actual file containing an image/text of any size.

Here is a basic example that breaks down a simple find command example into its respective elements:

find topdir2 -name "myfile.txt" -perm 644
  • topdir2 - the starting point of the search.
  • -name - A test expression.
  • "myfile.txt" - Value of the expression -name.
  • -perm - Another test expression.
  • 644 - The value of the expression -perm.


Test Expressions

Test expressions are used to filter the folders and files.

Filtering Empty File

The -empty option will search only empty files and directories. It does not need a value.

find . -empty -type f

Filtering by File Name

The expression -name <value> filter files and directories by file name. Regular expressions are not allowed for the <value>, but shell patterns (also called glob operators) are permitted, such as *, ?, or [].

find . -name '*.txt' find . -name 'image.jpg'

Filtering by File Path

The expression -path <value> filters files and directories by their file paths. Like -name, it does not accept regular expressions but shell patterns.

find . -path '**/topdir1/*.jpg'

Filtering Using a Regex

File name matches regular expression pattern using -regex <value>. This is a match on the whole path, not a search. For example, to match a file named ./foobar, you can use the regular expression .*bar. or .*b.*3, but not f.*r3

find . -regex '.*1.txt'

The positional option -regextype can be used before -regex, to specify the regex engine you want to use. To output a list of regex engines supported, run find . -regextype dummy. Example output:

find: Unknown regular expression type ‘dummy’; valid types are ‘findutils-default’, ‘ed’, ‘emacs’, ‘gnu-awk’, ‘grep’, ‘posix-awk’, ‘awk’, ‘posix-basic’, ‘posix-egrep’, ‘egrep’, ‘posix-extended’, ‘posix-minimal-basic’, ‘sed’.

The following example will find every txt and jpg file using egrep, the extended regular expression engine(ERE):

find . -regextype "egrep" -regex '.*(txt|jpg)$'

Case-insensitive searches can be performed by adding the prefix i to the above mentioned expressions. For example: -iname, -ipath, or -iregex.

Filtering by Type of File

The most common file types to filter on are:

  • f - File
  • d - Directory
  • l - Symbolic link

To search for more than one type at once, separate the options by a comma ,.

find . -name 'dir_a' -type d

Filtering by Permissions

Files can be filtered by whether they are -executable, -readable, or writeable for the current user. For additional granularity, you can use -perm <value>, where <value> can be:

  • A string beginning with / and followed by a series of rules using the OR Boolean operator. For example, -perm /u=w,g=e (writable by the owner, and executable by the group).
  • A string beginning with - and followed by a series of rules using the AND Boolean operator. For example, -perm -u=w,g=e (writable by owner, or executable by the group).
  • An octal number, for example: 644.

Filtering by Owner or Group

  • -user <value> where <value> is a username.
  • -group <value> where <value> is a groupname.


Action Expressions


Deleting Files

Deleting files and directories can be accomplished using the -delete option. The following command will delete all files and directories when their names begin with test.

find . -name "test*" -delete

WARNING: This will permanently delete your files. Use with caution, if at all.

Running a Command on Each Result

The -exec expression executes a command. The string {} is replaced by the current file name being processed everywhere it occurs in the arguments to the command. All following arguments are taken to be arguments to the command until an argument consisting of ; is encountered. Both of these constructions may need to be escaped with a backslash or quoted to protect them from expansion in the shell.

Running a Command in Working Directory

  • find . -exec basename '{} ';' - Run the command basename for every result of the search.
  • find . -exec bash -c 'basename "${0%.*}"' '{}' \; - The command bash -c will allow us to expand parameters. ${0%.*} is used here to remove the file extension from each result.
  • find . -name 'image.jpg' -exec file {} \; - Run the file command against all .jpg files returned from the search.

You can also use the expression -ok. It is the same as the -exec option, except that find will prompt you, asking if you really want to run the command. This confirmation will be asked for each result.

find . -name "image.jpg" -ok file {} \;

Running a Command in Starting Directory

The two expressions -execdir and -okdir work like -exec and -ok respectively, except that the commands won’t run in your current working directory, but in the starting directory (the first argument of find).

find topdir1/dir_b -exec bash -c 'basename "${0%*.}"' '{}' \;

Rename every .jpg file in the topdir1 directory with _old and keep same extension:

find topdir1 -name "image.jpg" -type f -execdir rename 's/\.jpg$/_old.jpg/' {} \;

Changing the Output

The following options will change the output of the search results:

  • -print - This is the default action even when not specified. It simply prints every result.
  • -ls - Works like the regular ls command.
  • -print0 - By default, the separator between different results is a \n newline character. With this option, the separator is a null character. Useful if you want to pipe results to xargs -0.
  • -printf - Output files with the information you need. For example: find . -printf %d %p will print the depth of the file in the file tree (%d) and the file name (%p).

Writing the Output to a File

You can also use a bunch of action expressions to write find’s output to a file. You just need to prefix the expression we saw above with a f. For example: -fls, -fprint, -fprint0 or -fprintf. The value of these expressions will be the file written.

Operators

When no operators are explicitly specified, the -and operator is used implicitly between each expression.

  • ! - Negate the expression following it.
  • -or or -o - Logical OR.
  • -and or -a - Logical AND.
  • , - Adding a comma is useful to use different sets of expressions while traversing the filesystem once.

    See the Examples section, below, for several use cases with operators.


Cookbook

The following examples make up the find command cookbook.

Find command structure

Here is a basic example that decomposes a simple find command construct and its respective elements:

find topdir2 -name "myfile.txt" -perm 644
  • topdir2 - the starting point of the search.
  • -name - A test expression.
  • "myfile.txt" - Value of the expression -name.
  • -perm - Another test expression.
  • 644 - The value of the expression -perm.

Basic find file commands

find / -name foo.txt -type f -print # full command
find / -name foo.txt -type f # -print isn't necessary
find / -name foo.txt # don't have to specify "type==file"
find . -name foo.txt # search under the current dir
find . -name "foo.*" # wildcard
find . -name "*.txt" # wildcard
find /users/al -name Cookbook -type d # search '/users/al' dir

find . -iname foo                 # find foo, Foo, FOo, FOO, etc.  
find . -iname foo -type d                    # same thing, but only dirs  
find . -iname foo -type f                    # same thing, but only files  

Search multiple directories

find /opt /usr /var -name foo.scala -type f

Files with different extensions

find . -type f \( -name "*.c" -o -name "*.sh" \) # "*.c" and "*.sh" files find . -type f \( -name "*cache" -o -name "*xml" -o -name "*html" \) # three patterns

Files that don't match a pattern (-not)

find . -type f -not -name "*.html" # find all files not ending in ".html"

Files by text in the file (find + grep)

find . -type f -name "*.java" -exec grep -l StringBuffer {} \; # find StringBuffer in all *.java files

find . -type f -name "*.java" -exec grep -il string {} \; # ignore case with -i option

find . -type f -name "*.gz" -exec zgrep 'GET /foo' {} \; # search for a string in gzip'd files

5 lines before, 10 lines after grep matches

find . -type f -name "*.scala" -exec grep -B5 -A10 'null' {} \;

Files and act on them (find + exec)

find /usr/local -name "*.html" -type f -exec chmod 644 {} \; # change files to mode 644
find htdocs cgi-bin -name "*.cgi" -type f -exec chmod 755 {} \; # change files to mode 755
find . -name "*.pl" -exec ls -ld {} \; # run ls command on files found
find . -type f -iname ".python-version" -print -exec sed -i 's/3.10.5/3.10.6/g' {} \;

Find and replace(sed)

find templates -name "*.html" -type f -exec sed -i 's/javascript:/#\"\ onclick=\"/g' {} \;

Find and copy

find . -type f -name "*.mp3" -exec cp {} ~/tmp/ \; # cp files to ~/tmp/

Copy one file to many directories

find dir1 dir2 dir3 dir4 -type d -exec cp header.shtml {} \;    # copy file  to dirs

Find and delete

find . -type f -name "Foo*" -exec rm {} \;   # remove "Foo*" files under current dir
find . -type d -name CVS -exec rm -r {} \;   # remove subdirectories named "CVS" under current dir
find . -name ".mediaartlocal" -type d -exec rm -r '{}' \; 

Files by modification time

sudo find / -type f -mmin -10
find . -mtime 1               # 24 hours
find . -mtime -7              # last 7 days
find . -mtime -7 -type f      # just files
find . -mtime -7 -type d      # just dirs

By modification time using a temp file

touch 09301330 foo   # 1) create a temp file with a specific timestamp
find . -mnewer foo   # 2) returns a list of new files
rm foo               # 3) rm the temp file

find and tar

find . -type f -name "*.java" | xargs tar cvf myfile.tar
find . -type f -name "*.java" | xargs tar rvf myfile.tar

find, tar, and xargs

find . -name -type f '*.mp3' -mtime -180 -print0 | xargs -0 tar rvf music.tar

 (-print0 helps handle spaces in filenames)

Rename

find . -name "folder.jpg" -type f -execdir mv {} cover.jpg \;
find ~ -iname "*new*" -exec mv -v '{}' /media/current-projects/ \;
find . -type f -name "*.wiki" -exec rename 's/\.wiki$/.md/' '{}' \;


Further Reading

Most the information & examples provided above were originally sourced from the following resources:

Tuesday, April 01, 2025

Customizing the Bash command prompt for vi editing mode

Customizing readline with inputrc

A brief introduction to the GNU Readline library, customizing user input using the .inputrc file, and the vi editing mode.

(Skip ahead to setting the mode indicator configuration.)

Introduction

I've been using vi/vim as my preferred editor long enough for the basic movement and editing keyboard shortcuts(also know as a key binding) to become second nature to me. Learning that the Bash shell supports multiple editing modes, including vi, gave me the opportunity to use this niche skill with other tools. I appreciate the idea that the key bindings that I had learned through using vi had some usefulness outside of a single application.

Bash supports this functionality through a library called Readline. Switching from Bash's default Emacs editing mode over to vi editing mode made sense, to me. Being able to switch tools and not have to remember a different set of keyboard shortcuts is convenient.

One feature that was missing, however, was a visual indicator of which mode I was currently in. For this, I needed to look at setting a couple of readline-functions that would modify the command line prompt to visually indicate whether one was in either insert or edit mode.

Setting the editing mode

By default, the Bash shell is set to use the readline Emacs editing mode.

To switch your command-line editing mode to vi, enter the following at the console:

set -o vi

If you are unsure which editing mode is currently being used in the shell, enter the command set -o from the command line. This will list the current state of the option-names and which editing mode is currently enabled:

The set command is a bash built-in that "sets or unsets values of shell options and positional parameters". See set --help for more details.

To make this change permanent, settings can be added in one of two files, depending on one's requirements.
For changing only the Bash shell to use the vi-editing mode, add the line set -o vi to the .bashrc.
Enabling the vi-editing mode for the Bash shell and any other tool that uses the Readline library, add the following line to the .inputrc file:

set editing-mode vi

From the readline(3) manpage:

Readline is customized by putting commands in an initialization file (the inputrc file). The name of this file is taken from the value of the INPUTRC environment variable. If that variable is unset, the default is ~/.inputrc. If that file does not exist or cannot be read, the ultimate default is /etc/inputrc.

Adding a Mode Indicator in the Prompt

Do you use set -o vi in your shell and can't remember whether you are in insert or edit/cmd mode? I do.

From the command line, activating a visual indicator at the prompt can be toggled with the following two commands:


bind 'set show-mode-in-prompt on'  
bind 'set show-mode-in-prompt off'  

From the man page:

If set to On, add a string to the beginning of the prompt indicating the editing mode: emacs, vi command, or vi insertion. The mode strings are user-settable (e.g., emacs-mode-string)

By default, this will place a string ((cmd)/(ins)) at the beginning of the prompt.

Moving between the modes can be done by pressing the Esc to enter the command mode and pressing the I key to resturn to insert mode.
This prompt can be customized with the vi-ins-mode-string and vi-cmd-mode-string variables.

If set from the command line, these indicators will persist only for the duration of that shell's existence.
To make the change permanent, add the following line to your .inputrc to place a visual indicator at the beginning of your prompt: set show-mode-in-prompt on

Personally, I prefer my command prompt slightly less cluttered. This is what my .inputrc file looks like for the mode indicators:


# Add mode indicators to command line
set show-mode-in-prompt on  
set vi-ins-mode-string "+"  
set vi-cmd-mode-string ":"  

This is my prompt with the customized mode indicators:

Different cursor shapes can also be configured for each mode. See the Arch Linux Readline wiki page for examples and a few more tips on modifying readline using the inputrc file.

vi editing mode

When opening a command line, the user will be in insert mode where text can be entered normally. When Esc or Ctrl+[ is pressed, an emulation of vi's cmd or normal mode will be entered and vi's keybindings can be used to move around and edit the command line. To return to insert mode, press the I key and text can be entered normally. Below is a list of some of the common key combinations:

  • 0 — Move to start of line
  • $ — Move to end of line
  • b — Move back a word
  • w — Move forward a word
  • e — Move to the end of the next word
  • dw — Delete a word
  • d$ — Delete to end of line
  • dd — Delete entire line
For a more complete list of commands, take a look at the Bash vi editing mode cheat sheet.

Thursday, December 13, 2018

Pre-flight steps for a Linux Mint desktop upgrade


Upgrading from Linux Mint from 18.3 to 19 on my systems was essentially painless.

There are a couple of things that I have gotten into the habit of doing prior to performing systems upgrades to provide me some peace of mind:

  1.  Back up your system.
  2.  Separately back up your .config and .local directories (I am selective in choosing which application preferences to preserve across machines and store them on dropbox).
  3.  Make a list of any PPAs you might be using and remove them from your sources.list or Update Manager. Here is a pointer to a script to get you started.
  4.  I store my dotfiles on a repo in GitHub (and replicate them out to GitLab and BitBucket), so that I can easily restore my bash, vim and tmux settings.
  5.  Make a list of your favorite apps (Personally, I use vimwiki stored in dropbox for my sysadmin notes).
  6.  I've found the following site very useful when turning up a new system: https://sites.google.com/site/easylinuxtipsproject/Home (It's Mint-centric and has some very sane recommendations. Don't run the suggestions blindly. Review them closely and see if it applies to you.
  7.  Maintain a separate /home partition so that you can easily restart, or completely blow away, an installation if you need to without fear of losing your personal data.

This may sound like a lot of work to those just getting started in the Linux/Mint world; but, some conscientious janitorial work upfront saves one from frustration down the road.

Hope this helps. Enjoy the upgrade!

Tuesday, July 19, 2016

Switching between Java versions in Debian based distributions

Switching between installed Java versions can be accomplished using the update alternatives command.

To get a list of your installed Java platforms, run the following command from the terminal:

sudo update-alternatives --config java

This will give you a list output similar to this:

There are 2 choices for the alternative java (providing /usr/bin/java).
   Selection                             Path                                   Priority         Status
  ---------------------------------------------------------------------------------------------
  0            /usr/lib/jvm/java-8-oracle/jre/bin/java                1081            auto mode
*1            /usr/lib/jvm/java-7-openjdk-i386/jre/bin/java    1071       manual mode
  2           /usr/lib/jvm/java-8-oracle/jre/bin/java                 1081       manual mode
Press enter to keep the current choice[*], or type selection number:

In this case, the Open JDK 6 version is running. To switch to the Open JDK version 7, you would select option 1 at the prompt and press the Enter key.

You will now be running the OpenJDK 7 version. No other changes will be needed to switch your Java versions.