[Tip] Useful Bash functions and aliases

Difficulty: ★★★☆☆


DISCLAIMER

The shell code below is to be added to one’s ~/.bashrc file and is only guaranteed to work with GNU bash. I do not use and have no experience with zsh.

Many people ─ especially the resident newbies ─ prefer zsh as an interactive shell because of the fact that it is highly themeable. Yet, at the basis, like most distributions, Manjaro still uses bash for its system initialization scripts, because bash is very powerful and it has stood the test of time as a reliable shell.

Nowadays, zsh is the Manjaro default interactive shell for new installations ─ even if only for its visual appeal ─ but back when I first installed Manjaro now about two and a half years ago (at the time of my writing this), bash was still the default interactive shell for unprivileged user accounts, and as a 20-plus-year GNU/Linux veteran, I saw no need to change that.

Therefore, as the disclaimer says, if you are using zsh, then the following code may not be of interest to you. But if you do still use bash and you find this code interesting, then feel free to implement it, modify it, or whatever. It’s all GPL, homie! :wink:

The default ~/.bashrc that ships with Manjaro already contains some aliases and functions ─ probably literally taken over from Arch, but if I’m wrong about that, then feel free to correct me :wink: ─ but I have added a couple more of my own, based upon the needs of the moment. I will give a brief description with each of them below. :wink:

Perhaps first a word about the difference between aliases and shell functions.

An alias is in fact only a shorthand for simple commands that you use a lot but would take too much time to type in every time. An alias can be a compound command ─ it can be made up of multiple existing commands ─ but it cannot be used for the manipulation of variables.

An alias always comes in the form… :arrow_down:

alias name-of-your-alias-here='command(s)-to-be-executed'

A shell function on the other hand is much more powerful. It is very much like a shell script that you store in a separate file ─ and come to think of it, you can actually do that with functions as well if you source the file that contains the function(s) from within your ~/.bashrc ─ but there are two main differences…:

  • A shell script is always executed in a subshell, not in the running shell.
  • A shell script is executed from an external file, which first needs to be opened and read into memory.

If on the other hand you include the code in your ~/.bashrc, then whenever you open a terminal window or log in at a tty, the code will already be in memory and can be executed right away, just as if it were an internal command of the bash executable itself.

Like I said, Manjaro already ships a ~/.bashrc with some aliases and functions in it by default, and there are also yet other places where shell configuration takes place, both system-wide and in your own user account ─ e.g. ~/.bash_profile and/or ~/.profile. So I’m only going to be posting some things that I myself have added to my ~/.bashrc for my convenience.

Here goes… :wink:


This here is an alias that Plasma users will probably find useful. It restarts the compositor, which you may have already been needing to do in the last two or three Plasma releases because of the bug that leaves ghost images in the compositor’s memory buffer. :arrow_down:

alias restart-compositor='qdbus org.kde.KWin /Compositor suspend && qdbus org.kde.KWin /Compositor resume'

The following two are also useful for Plasma users, because it allows you to lock your widgets or unlock them again from the command line. :arrow_down:

alias widgets-lock='qdbus org.kde.plasmashell /PlasmaShell evaluateScript "lockCorona(true)"'
alias widgets-unlock='qdbus org.kde.plasmashell /PlasmaShell evaluateScript "lockCorona(false)"'

As you can see, it’s a lot easier to type the name of the aliases than to type the full qdbus commands. :wink:

As for the functions, you can invoke a function by simply typing its name as a command at the prompt, just as you do with an alias.

The first function below modifies the man command so that man pages can be viewed in color. :arrow_down:

man ()
{
    LESS_TERMCAP_md=$'\e[01;37m' \
    LESS_TERMCAP_me=$'\e[0m' \
    LESS_TERMCAP_so=$'\e[01;44;32m' \
    LESS_TERMCAP_se=$'\e[0m' \
    LESS_TERMCAP_us=$'\e[01;33m' \
    LESS_TERMCAP_ue=$'\e[0m' \
    command man "$@"
}

export -f man

I have created and refined the following two shell functions in the last couple of days, and they have been properly tested. They work as regular shell commands, and they take a directory name ─ even one that contains spaces, if you properly escape them or use quotes around the name ─ as an argument.

If you do not supply any command-line argument, then they will be executed in the current working directory ─ they do not descend into subdirectories below the current or specified directory. They both also ask for confirmation before doing their stuff so that you can abort the operation if you’re not sure, and they both also provide for an exit code ─ 0 for success (even if you abort the operation) and 1 for incorrect usage.

The purpose of these two functions is to translate spaces in filenames into underscores, and vice versa. The name of either function should make it clear enough which is which. :wink: :arrow_down:

spaces2underscores ()
{
    targetdir="$*"
    if [ ! -z "$1" ]
    then
       if [ -d "${targetdir}" ]
       then
          oldpwd=$(pwd)
       else
          echo "Not a valid directory."
          return 1
       fi
    fi
    read -n1 -p "Rename all files in ${targetdir}? [y/N]"
    case ${REPLY} in
    "Y" | "y" )
       cd "${targetdir}"
       fncounter=0
       for fn in *
       do
          newfn=$(printf "${fn}" | tr ' ' '_')
          if [ "${newfn}" != "${fn}" ]
          then
             mv "${fn}" "${newfn}"
             fncounter=$((${fncounter}+1))
          fi
       done
       cd "${oldpwd}"
       echo "Successfully replaced spaces by underscores in ${fncounter} filename(s)."
       echo
       ;;
    *         )
       echo "Operation aborted."
       echo
       return 0
       ;;
    esac
    unset targetdir oldpwd REPLY fncounter fn newfn
}


underscores2spaces ()
{
    targetdir="$*"
    if [ ! -z "$1" ]
    then
       if [ -d "${targetdir}" ]
       then
          oldpwd=$(pwd)
       else
          echo "Not a valid directory."
          return 1
       fi
    fi
    read -n1 -p "Rename all files in ${targetdir}? [y/N]"
    case ${REPLY} in
    "Y" | "y" )
       cd "${targetdir}"
       fncounter=0
       for fn in *
       do
          newfn=$(printf "${fn}" | tr '_' ' ')
          if [ "${newfn}" != "${fn}" ]
          then
             mv "${fn}" "${newfn}"
             fncounter=$((${fncounter}+1))
          fi
       done
       cd "${oldpwd}"
       echo "Successfully replaced underscores by spaces in ${fncounter} filename(s)."
       echo
       ;;
    *         )
       echo "Operation aborted."
       echo
       return 0
       ;;
    esac
    unset targetdir oldpwd REPLY fncounter fn newfn
}

export -f spaces2underscores underscores2spaces

As you can see, I have put an export -f command underneath each code block. This command is not part of the functions themselves, but instead, it makes sure that the functions are exported into any subshells that may get started, just as you can export variables.

You also don’t need to write an export -f command for each of the code blocks. You can define all of your functions and then simply put a single export -f line at the bottom that exports all of the functions in one go, like so… :arrow_down:

export -f this-function-here that-function-there another-function-here ...

I hope this was useful, and as always, I am looking forward to hearing your expert opinions in the comment section below. Please like and subscribe, hit that notification bell, and don’t forget to share with all your friends on FaceTube. :stuck_out_tongue:

:crazy_face: :rofl:

6 Likes

why i don’t use functions in dot file but create scripts in ~/.local/bin/ ?

  • the code will not already be in memory : for me is best and i can wait 0.2 second for run a command in my console)
  • I use fish and some time zsh : scripts with shebang (bash) are always good
  • no limited to bash only
  • with 90 scripts :upside_down_face:, it’s more simple for me to manage/edit than having everything in one file

Well, that in itself would not be a problem. You could create a directory ─ e.g. ~/.bashrc.d ─ and put individual files with functions in there, and name each file after the function it contains. Then, in your ~/.bashrc itself, you would have something like the following… :arrow_down:

for fileinc in ~/.bashrc.d/*
do
   source ${fileinc}
   export -f ${fileinc}
done
unset fileinc

The result would be the same as if all of those functions were included in your ~/.bashrc itself. But of course, if you don’t want to have all that code in memory ─ not that it would consume a lot of memory, given that it’s only plain text ─ then that is your prerogative. And if you use another shell than bash, then that too is your prerogative.

I’m only sharing, and perhaps my code can inspire people to write other things, or to learn about shell scripting. :wink:

1 Like

the final result is the same :wink: we code small utilities that are very useful for us.
I still have in my .zsh

for fileinc in $HOME/.config/${SHELL##*/}/*.inc; do
        [ -r "$fileinc" ] && .  "$fileinc"
done
unset fileinc

But, it was easy to add a shebang to each file to be independent of my user shell (and the script language)

1 Like

1 Like