Linux productivity tools and practices for researchers
While working on the Research Computing clusters you will probably find yourself entering the same commands over and over again. There will also be times when you will run multiple commands to get a result. These two types of actions reduce your productivity and they can be partially eliminated.
Commonly repeated commands should be replaced by an alias, which is a short name for the command. An alias can also combine multiple commands into one. Shell functions are like aliases but they are more flexible because they accept command-line parameters.
This page illustrates how to use aliases and shell functions to improve productivity. While numerous examples are given, for the greatest impact you should create new ones based on your own work.
Let’s illustrate the process of creating an alias using this popular command:
$ squeue -u <YourNetID>
Instead of typing this every time, we will use the much shorter alias of sq
:
$ alias sq='squeue -u <YourNetID>'
Be sure to replace <YourNetID>
with your actual NetID (e.g., aturing). After defining this alias, one can type sq
instead of the much longer squeue -u <YourNetID>
:
$ sq
Note that aliases defined on the command line will only be available in the current shell. To make them permanent see the next section.
To make your aliases and shell functions available each time you log in, store them in your ~/.bashrc
file. Here is the contents of ~/.bashrc
for a new account:
$ cat ~/.bashrc
# Source global definitions
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi
# Uncomment the following line if you don't like systemctl's auto-paging feature:
# export SYSTEMD_PAGER=
# User specific aliases and functions
Make your aliases and shell functions permanent by adding them to your ~/.bashrc
file and then sourcing the file. For instance, use a text editor like vim or emacs to add the sq
alias:
$ nano ~/.bashrc # or vim, emacs, micro, MyAdroit, etc.
Add this line:
# User specific aliases and functions
alias sq='squeue -u <YourNetID>'
Save the changes and then return to the command line. Make the new alias available in the current shell by “sourcing” your ~/.bashrc
file:
$ source ~/.bashrc
Now try out the alias:
$ sq
You only need to source your ~/.bashrc
file when you add an alias in the current session. When you first log in all aliases will be available. Once you have built up your ~/.bashrc
file it will contain aliases and shell functions like those in the example myshortcuts.sh file in this repo.
Consider creating a GitHub repo containing your .bashrc
file. Make all of your changes to that file in the repo and then pull it down to different machines. Consider making an alias for the sync:
alias sync="wget -O .bashrc https://raw.githubusercontent.com/aturing/shell_configs/refs/heads/main/.bashrc"
One can use the hostname
command and if
statements to deal with differences between clusters. For an example of this, see the checkquota section below.
To see your aliases use the alias
command:
$ alias
alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'
alias grep='grep --color=auto'
alias l.='ls -d .* --color=auto'
alias ll='ls -l --color=auto'
alias ls='ls --color=auto'
alias sq='squeue -u aturing'
alias vi='vim'
alias which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'
Note that some aliases are automatically created. The above is saying that if we run the l.
command, for example, the shell is really running ls -d .* --color=auto
.
Be sure not to name an alias after an existing command. If your shell is not behaving as expected it may be because you created an alias using the name of a pre-existing command. Try running your proposed alias name on the command line to see if it is already a command before creating a new alias.
Aliases take precedence over commands loaded via modules. This is illustrated below with the intel
module:
$ module load intel/19.1.1.217
$ icc
$ module purge
$ alias icc='ps -u $USER'
$ module load intel/19.1.1.217
$ icc
If you run the commands above, icc
will not be the Intel C compiler as one may expect. Be careful of this and put some effort into choosing alias names. When choosing a name for an alias, always make sure that it is not already in use:
$ cq
-bash: cq: command not found
We see that cq
is not in use so this could be used as an alias for the checkquota
command:
alias cq='checkquota'
To see the aliases that you are using, run this command:
$ alias
To turn off a specific alias for the current shell session:
$ unalias <alias>
To turn off for a single command:
$ \<alias>
Return to the Word of caution section above and try running \icc
at the very end.
On a QWERTY keyboard the home keys are A, S, D, F, J, K, L and the semicolon. Since your fingers typically rest on these keys they make great alias names.
Consider the system below based on the home keys:
export EDITOR=/usr/bin/vim # or emacs or nano
alias ll='ls -ltrh'
alias jj='cat -- "$(ls -t | head -n 1)"'
alias kk='cat -- "$(ls -t | head -n 2 | tail -n 1)"'
alias ff='$EDITOR -- "$(ls -t | head -n 1)"'
alias dd='$EDITOR -- "$(ls -t | head -n 2 | tail -n 1)"'
The ll
alias lists the files in the current directory in long format and sorts them by modification time with the newest at the bottom.
The jj
command prints the contents of the newest file in the current working directory to the terminal while kk
prints out the second newest file. jj
is arguably the most useful alias on this entire page. Start using it! The ff
command loads the newest file in your specified text editor while dd
loads the second newest file. The routine use of ll
, jj
and ff
can save you lots of time. Note that dd
overwrites an existing command but because the original dd
is obscure, this can be overlooked. If you are left-handed then you may consider transposing the aliases (e.g., interchange jj
with ff
).
Note that aa
and ss
are waiting to be defined. While ss
is a pre-existing command, it is obscure and can be overwritten. gg
and hh
are also available.
The meaning of --
in the commands above is explained here.
Here are some shell functions and aliases for creating directories and moving around the filesystem:
mk() { mkdir -p "$1" && cd "$1"; }
cl() { cd "$1" && ll; } # uses alias defined above
alias ..='cd .. && ll'
alias ...='cd ../.. && ll'
alias pwd='pwd -P'
mk
is the first shell function that we have encountered. The existence of $1
in the body of the function corresponds to the input paramter. The &&
operator ensures that the command on the right is only executed if the command on the left is successful. The mk
function makes a directory and then cd’s into that directory:
$ pwd
/home/aturing
$ mk myproj
$ pwd
/home/aturing/myproj
The cl
function cd’s into a specified directory and runs the ll
alias. The ..
alias above allows us to type 2 keys to go up a level and list the directory contents.
Note that there are many pre-defined functions.
$ set | less
Do not load environment modules in your .bashrc
file. We make this recommendation because users forget that they have added these commands. Use aliases instead.
Here are some aliases for quickly working with modules:
alias ma='module avail'
alias mp='module purge'
alias ml='module list'
alias mla='module load anaconda3/2024.6'
alias mlc='module load cudatoolkit/12.6'
Another approach would be to define an alias like this:
alias modl='module load'
Then use it as follows:
$ modl anaconda3/2023.3
When searching for files, one often wants to do a case-insensitive search while suppressing error messages. The shell function below does this. If only one command-line parameter is specified then it begins the search in the current directory. One can also explicitly specify the path.
myfind() {
if [ "$#" -eq 1 ]; then
find . -iname \*"$1"\* 2>/dev/null
else
[ -d "$1" ] && find "$1" -iname \*"$2"\* 2>/dev/null
fi
}
Here is the example usage:
$ ssh <YourNetID>@della.princeton.edu
$ myfind /usr/local cublas
The above says “find all files in /usr/local
and its sub-directories that contain the pattern ‘cublas’ (case insensitive) while suppressing error messages (typically related to file permissions)”.
Or equivalently:
$ ssh <YourNetID>@della.princeton.edu
$ cd /usr/local
$ myfind cublas
This function can be added to the shell configuration file (~/.bashrc
on Linux or ~/.bash_profile
on macOS) on your local machine (e.g., laptop) to create an SSH tunnel for using Tensorboard (see directions for Tensorboard) on della-gpu:
board() {
case "$#" in
0)
echo "Missing port. Tunnel not created."
;;
1)
ssh -N -f -L "$1":127.0.0.1:"$1" ${USER}@della-gpu.princeton.edu
echo "Created SSH tunnel using port $1"
;;
2)
ssh -N -f -L "$1":"$2":"$1" ${USER}@della-gpu.princeton.edu
echo "Created SSH tunnel using port $1 and host $2"
;;
*)
echo "Too many command-line arguments ("$#"). Tunnel not created."
esac
}
If running Tensorboard on the head node then use:
$ board 9100
If running on a compute node then use, for example:
$ board 9100 della-l09g6
Be sure to specify the correct port and host in the commands above for your case. If the username on your local machine (where the board function is defined) is not the same as your Princeton NetID then you will need to replace ${USER}
with your NetID.
The shell functions and alias below can be used to list your enumerated Conda environments (conen), activate an environment by number (conac), deactivate the current environment (conde) and remove or delete an environment (conrm):
conen() {
if [ $(module -l list 2>&1 | grep -c anaconda3) -eq 0 ]; then
echo "Loading anaconda3 module ..."
module load anaconda3/2023.3
fi
conda info --envs | grep . | grep -v "#" | cat -n
}
conac() {
name=$(conda info --envs | grep -v "#" | awk 'NR=="'$1'"' | tr -s ' ' | cut -d' ' -f 1)
conda activate $name
}
alias conde='conda deactivate'
conrm() {
name=$(conda info --envs | grep -v "#" | awk 'NR=="'$1'"' | tr -s ' ' | cut -d' ' -f 1)
conda remove --name $name --all -y -q
echo; conen; echo
}
Display your enumerated Conda environments (and load the anaconda3 module if necessary). This is similar to conda env list
which can also be used.
Activate an environment by number. The conen
command enumerates your environments. This command is similar to conda activate <name>
, which can also be used, but it takes a number instead of a name.
Deactivate the current environment.
Remove an environment by the number given by conen
. This command is similar to conda remove --name <name> --all
but it works by number instead of name.
A session using the shortcuts above might look like this:
[aturing@tigergpu ~]$ conen
Loading anaconda3 module ...
1 py36 /home/aturing/.conda/envs/py36
2 pytools-env /home/aturing/.conda/envs/pytools-env
3 tf2-gpu /home/aturing/.conda/envs/tf2-gpu
4 torch-env /home/aturing/.conda/envs/torch-env
5 base * /usr/licensed/anaconda3/2023.3
(base) [aturing@tigergpu ~]$ conac 4
(torch-env) [aturing@tigergpu ~]$
(torch-env) [aturing@tigergpu ~]$ conde
(base) [aturing@tigergpu ~]$ conrm 1
# the py36 environment would be deleted
Note that aliases do not work in Slurm scripts. You will need to explicitly load your modules in Slurm scripts.
If you submit a lot of jobs with commands like sbatch job.slurm
or sbatch submit.sh
then you may try calling all your Slurm scripts by the same name (e.g., job.slurm
) and then introducing this alias:
SLURMSCRIPT='job.slurm'
alias sb='sbatch $SLURMSCRIPT'
Jobs can then be submitted with:
$ sb
You can distinguish different jobs by setting the job name in the Slurm script:
#SBATCH --job-name=low-temp # create a short name for your job
The alias below submits the job and then launches watch
. This allows one to know when short test jobs start running:
alias sw='sbatch $SLURMSCRIPT && watch -n 1 squeue -u $USER'
To exit from watch
hold down [Ctrl] and press [c].
Show the state of your running and pending jobs:
alias sq='squeue -u $USER'
See the expected start times of pending jobs:
alias sqs='squeue -u $USER --start'
Watch your jobs in the queue (useful for knowing when test jobs run):
alias wq='watch -n 1 squeue -u $USER'
This will create an alias which will display the result of the squeue command for a given user and update the output every second. This is very useful for monitoring short test jobs. To exit from watch
hold down [Ctrl] and press [c].
Use the aliases below to work interactively on a compute node (with and without a GPU) for 5 minutes:
alias cpu5='salloc --nodes=1 --ntasks=1 --mem=4G --time=00:05:00'
alias gpu5='salloc --nodes=1 --ntasks=1 --mem=4G --time=00:05:00 --gres=gpu:1'
Note that you can modify the values of the parameters. For instance, for a 20-minute CPU allocation:
$ cpu5 -t 20
For more on salloc
see this page.
It is often useful to SSH to the compute node where your job is running. From there one can inspect memory usage, thread performance and GPU utilization, for instance. The following function will connect you to the compute node that your most recent job is on:
goto() { ssh $(squeue -u $USER -o "%i %R" -S i -h | tail -n 1 | cut -d' ' -f2); }
The function above uses squeue
to list all your job id’s in ascending order along with the corresponding node where the job is running. It then takes the last row, extracts the node and calls ssh
on that. This method will not work when multiple nodes are used to run the job.
Running mycancel
will automatically find the job id of your most recent job and cancel the job:
mycancel() { scancel $(squeue -u $USER -o "%i" -S i -h | tail -n 1); }
The function above uses squeue
to list all your job id’s in ascending order and then it passes the last one to scancel
. Later in this repo we present implementations of mycancel
in Python and C++. The implementation above is of course in Bash.
Your job priority is in part determined by the cluster usage of other members of your Slurm group over the past 30 days. The function below can be used to see usage by user:
hog() {
start_date=$(date -d"30 days ago" +%D);
account=$(sshare | grep $USER | awk '{print $1}' | head -n 1);
sreport user topusage start=${start_date} end=now TopCount=100 accounts=${account} -t hourper --tres=cpu;
}
A small number of users have multiple Slurm accounts. Modifications to the above function may be needed for these users.
Previoulsy we used a lengthy shell function for this. That function helped many users so now it has been promoted to a system command:
$ shistory
Give the command above a try. To see the help menu: $ shistory -h
.
If you set #SBATCH --mail-user
in your Slurm script then you will receive an efficiency report by email. The following command can also be used from the directory containing the slurm output file (e.g., slurm-3741530.out
):
eff() { jobstats $(ls -t slurm-*.out | head -n 1 | tr -dc '0-9'); }
The eff
function figures out the job id and runs jobstats
on that.
Your fairshare value plays a key role in determining your job priority. The more jobs you or members of your Unix group run over the last 30 days, the lower your fairshare value. Fairshare varies between 0 and 1 with 1 corresponding to the largest job priority.
alias fair='echo "Fairshare: " && sshare | grep $USER | awk '"'"'{print $(NF)}'"'"''
To learn more about job priority see this page.
alias smi='nvidia-smi'
alias wsmi='watch -n 1 nvidia-smi'
After submitting a GPU job it is common to run goto
followed by wsmi
on the compute node. This allows one to examine GPU utilization. To exit from watch
hold down [Ctrl] and press [c].
if [[ $(hostname) == adroit* ]]; then
alias a100='ssh adroit-h11g1'
fi
If you have a job running on adroit-h11g1
then with the alias above you can quickly connect.
To get the runtime limits for the different job partitions (QOS) on Della:
if [[ $(hostname) == della* ]]; then
alias limits='cat /etc/slurm/job_submit.lua | egrep -v "job_desc|--" | awk '"'"'/_MINS/ \
{print " "$1,"<=",$3" mins ("$3/60 " hrs)"}'"'"''
fi
While X11 forwarding (via ssh -X
) is usually sufficient to work with graphics on the HPC clusters, TurboVNC is a faster alternative. See the bottom of this page for shells function to ease the setup.
The checkquota
command provides information about available storage space and number of files. While it’s only 10 characters you may consider reducing it to 2 since it is used a lot:
alias cq='checkquota'
Another tip is to put the following in your ~/.bashrc
or myshortcuts.sh
file to see your remaining space each time you log in:
if [ ! -z "$PS1" ]; then
case $(hostname) in
adroit?)
echo
timeout 5 checkquota | head -n 3
echo ;;
della*.*)
echo
timeout 5 checkquota | head -n 3
echo ;;
stellar-intel*)
echo
timeout 5 checkquota | head -n 3
echo ;;
tigercpu*)
echo
timeout 5 checkquota | head -n 3
echo ;;
tigergpu*)
echo
timeout 5 checkquota | head -n 3
echo ;;
tigressdata*)
echo
timeout 5 checkquota | head -n 7
echo ;;
traverse*.*)
echo
timeout 5 checkquota | head -n 3
echo ;;
esac
fi
To learn about interactive shells and $PS1
see this page. Learn about .bashrc
and .bash_profile
here.
To list the size of each directory to know which files to delete to free space:
alias dirsize='du -h --max-depth=1 | sort -hr'
Get a weather report for Princeton, NJ (UPDATE: this is sometimes not available due to overuse):
alias wthr='/usr/bin/clear && date && curl -s wttr.in/princeton'
This alias was contributed by T. Comi.
$ wthr
Tue Oct 3 14:14:37 EDT 2023
Weather report: princeton
\ / Sunny
.-. +77(80) °F
― ( ) ― ↘ 2 mph
`-’ 9 mi
/ \ 0.0 in
┌─────────────┐
┌──────────────────────────────┬───────────────────────┤ Tue 03 Oct ├───────────────────────┬──────────────────────────────┐
│ Morning │ Noon └──────┬──────┘ Evening │ Night │
├──────────────────────────────┼──────────────────────────────┼──────────────────────────────┼──────────────────────────────┤
│ Mist │ Mist │ \ / Sunny │ \ / Clear │
│ _ - _ - _ - 55 °F │ _ - _ - _ - 62 °F │ .-. +78(82) °F │ .-. 68 °F │
│ _ - _ - _ ↘ 2-3 mph │ _ - _ - _ ↘ 3 mph │ ― ( ) ― ↑ 1-3 mph │ ― ( ) ― ↑ 3-6 mph │
│ _ - _ - _ - 6 mi │ _ - _ - _ - 6 mi │ `-’ 6 mi │ `-’ 6 mi │
│ 0.0 in | 0% │ 0.0 in | 0% │ / \ 0.0 in | 0% │ / \ 0.0 in | 0% │
└──────────────────────────────┴──────────────────────────────┴──────────────────────────────┴──────────────────────────────┘
┌─────────────┐
┌──────────────────────────────┬───────────────────────┤ Wed 04 Oct ├───────────────────────┬──────────────────────────────┐
│ Morning │ Noon └──────┬──────┘ Evening │ Night │
├──────────────────────────────┼──────────────────────────────┼──────────────────────────────┼──────────────────────────────┤
│ Mist │ \ / Sunny │ \ / Sunny │ \ / Clear │
│ _ - _ - _ - 57 °F │ .-. +78(84) °F │ .-. +75(77) °F │ .-. 64 °F │
│ _ - _ - _ ↗ 1 mph │ ― ( ) ― ↑ 3 mph │ ― ( ) ― ↖ 3-8 mph │ ― ( ) ― ↖ 6-12 mph │
│ _ - _ - _ - 6 mi │ `-’ 6 mi │ `-’ 6 mi │ `-’ 6 mi │
│ 0.0 in | 0% │ / \ 0.0 in | 0% │ / \ 0.0 in | 0% │ / \ 0.0 in | 0% │
└──────────────────────────────┴──────────────────────────────┴──────────────────────────────┴──────────────────────────────┘
┌─────────────┐
┌──────────────────────────────┬───────────────────────┤ Thu 05 Oct ├───────────────────────┬──────────────────────────────┐
│ Morning │ Noon └──────┬──────┘ Evening │ Night │
├──────────────────────────────┼──────────────────────────────┼──────────────────────────────┼──────────────────────────────┤
│ \ / Sunny │ \ / Sunny │ \ / Sunny │ \ / Clear │
│ .-. 62 °F │ .-. +73(77) °F │ .-. 69 °F │ .-. 60 °F │
│ ― ( ) ― ↖ 2-3 mph │ ― ( ) ― ↖ 4 mph │ ― ( ) ― ↖ 7-14 mph │ ― ( ) ― ↖ 5-11 mph │
│ `-’ 6 mi │ `-’ 6 mi │ `-’ 6 mi │ `-’ 6 mi │
│ / \ 0.0 in | 0% │ / \ 0.0 in | 0% │ / \ 0.0 in | 0% │ / \ 0.0 in | 0% │
└──────────────────────────────┴──────────────────────────────┴──────────────────────────────┴──────────────────────────────┘
Location: Princeton, Mercer County, New Jersey, United States of America [40.3492744,-74.6592957]
Follow @igor_chubin for wttr.in updates
Shadowing is the remapping of a command with different parameters:
alias vi='vim'
alias top='htop'
alias cmake='cmake3'
alias R='R --vanilla --quiet'
alias ccat='/usr/licensed/anaconda3/2023.3/bin/pygmentize'
The ccat
alias is like the cat
command but with syntax highlighting of Python files and more (e.g., $ ccat myscript.py
).
Use this alias to watch anything:
alias wa='watch -n 1'
Use it as follows:
$ wa nvidia-smi # monitor GPU utilization
$ wa ls -l # if downloading a file
$ wa date # see a running clock
$ wa squeue --me # see status of jobs
$ wa free # monitor free memory
Add these aliases to ~/.bash_profile
on your Mac:
alias wget='curl -O'
alias ldd='otool -L'
This will allow you call wget
as you would on a Linux machine. The wget
command can be used to download files from the internet.
Try running the following command on your history to look for common commands to create an alias for:
$ history | awk '{CMD[$2]++;count++;}END { for (a in CMD)print CMD[a] " " CMD[a]/count*100 "% " a;}' | grep -v "./" | column -c3 -s " " -t | sort -nr | nl | head -n10
See this page for more aliases and shell functions. To see the aliases used by all users on the cluster, run this command:
$ find /home -maxdepth 2 -type f -name '.bashrc' 2>/dev/null | xargs grep 'alias' | grep -v 'User specific aliases and functions' | sed 's/^.*\(alias\)/\1/' | sort | uniq | cat -n
Your ~/.bash_history
file stores the commands you ran. The settings below increase the number of entries allowed in this file, include a timestamp with each command and combine history from different shells.
export HISTSIZE=50000 # lines of history to keep
export HISTFILESIZE=50000 # keep extended history file
#export HISTTIMEFORMAT='%F %T ' # show date and time of past commands
PROMPT_COMMAND='history -a' # append current session to history
Enhance your shell with these settings:
shopt -s histappend # all shells write to same history
shopt -s checkjobs # check if background jobs are running on exit
shopt -s cdspell # guess misspelled cd commands
shopt -s autocd # change directory w/o cd if entry is invalid
shopt -s extglob # enable extended glob patterns
For all the possible options and their meanings see man bash
.
With shopt -s autocd
one can cd
without typing cd
:
$ pwd
/home/aturing
$ ls
myproj file.txt
$ myproj
cd myproj
$ pwd
/home/aturing/myproj
With shopt -s cdspell
one can do:
$ cd mypoj
myproj
$ pwd
/home/aturing/myproj
This enhancement is only somewhat useful since users should be using tab completion.