Skillfade Logo

Zsh - Tips and Tricks

⏳ 11 min read

Table of Contents

    Cybersecurity Illustration

    I can smell the neon air, electric in your lungs, as you breach the firewall on the forty-seventh floor of the corporatised sky-scape. You’ve got wires humming beneath your skin, data tiles clicking like the floor beneath a street vendor’s stall in a rain-beset alley. Somewhere deep in the network’s guts an alert siren pulses, red LEDs dancing across a rack of switches. You are hunters in this digital jungle. Zsh is your katana, shimmering in the low-light glow of terminal windows.

    The circuits hum with binary, the echo of SSH sessions and VPN tunnels folding around you like a cloak. You taste copper and ozone as you navigate through aliases, prompts, shell functions, completions. Every keystroke matters. Every configuration line can betray your intent or save your hide. You are balancing on the razor’s edge between chaos and order, between security disaster and system mastery. Zsh is not just a shell. It is your armour. It is your weapon.


    Mastering Zsh: Hands-on Tips & Tricks for Cybersecurity Professionals

    Below are gritty, structured workflows, command recipes, configurations drawn from smoke-filled server rooms. Use them in authorised, legal labs only. When you run snippets marked risky, make sure you own the environment.


    1. Shell Hardening and Secure Configuration

    Goal: Lock down your Zsh so even if someone steals your terminal session or your .zshrc, they cannot easily abuse it.

    How-To:

    1. Minimal Startup Files
      - Use $ZDOTDIR/.zshenv only for variables that must apply universally (even during scripts). Keep $ZDOTDIR/.zshrc for interactive shell configuration.
      - In .zshenv, avoid commands that execute external programs that may be manipulated.

    2. Immutable .zshrc and other config files

    bash
       chmod 400 ~/.zshrc ~/.zshenv ~/.zlogout
       chattr +i ~/.zshrc  # on ext4, etc., Linux only
    

    WARNING: Commands like chattr +i can be dangerous on file systems that don’t support them or for users who need to modify. Run only in authorised environments.

    1. Restrict Dangerous Builtins
      - In .zshrc, unset potentially dangerous builtins:
    bash
         unset -f load
         unset -f eval
    
    • Or restrict execution path to only trusted directories.
    1. Set Secure PATH
    bash
       typeset -ga PATH
       PATH=(/usr/local/bin /usr/bin /bin)  # override potentially compromised paths
       path=(/usr/local/bin /usr/bin /bin)
    
    1. Enable Secure Prompt (pwd, user, host) but without data leaks
      - Custom prompt that omits full paths or user home directories in shared terminals:
    zsh
         setopt PROMPT_SUBST
         PROMPT='%n@%m:%~ %# '
    

    Checklist:

    • [ ] Are .zshrc, .zshenv, .zlogout permissions 400 or stricter?
    • [ ] Are builtins like eval unset or blocked?
    • [ ] Is PATH only trusted directories?
    • [ ] Prompt does not leak secrets (full path, hostnames, etc.) in shared logs?

    Mini-lab:

    • Create two users on a test VM.
    • Modify .zshrc for one to use risky PATH (like /tmp) and another with locked PATH.
    • Try executing a malicious binary in /tmp from both accounts.
    • Observe differences.

    2. Aliases, Functions & Workflow Automation for Incident Response

    Move fast. You’ll want quick commands when alarms deploy.

    How-To:

    1. Alias Templates
    zsh
       alias lsofnet='lsof -i -P -n'
       alias sniff='sudo tcpdump -nn -i any'
       alias ports='netstat -tulpen'
    
    1. Use Functions for Complex Tasks
    bash
       # WARNING: This function executes network traffic capture and may capture sensitive data. Use only in legal, controlled settings.
       capture_http() {
         local iface=${1:-eth0}
         sudo tcpdump -i "$iface" -nn -s 0 port 80 or port 443 -w capture_$(date +%s).pcap
       }
    
    1. Dynamic Prompt with Incident Mode
      - Add environment-based flags:
    zsh
         if [[ -n "$INCIDENT_MODE" ]]; then
           PROMPT='%F{red}INCIDENT:%f %n@%m:%~ %# '
         fi
    
    1. SSH Helpers
      - Function to spin up SSH with tunneling, proxy, jumphosts:
    bash
         ssh_jump() {
           ssh -J user@jumphost "$@"
         }
    

    Workflow:

    Step What to Do
    1 On detection of anomaly (alert, strange traffic), set INCIDENT_MODE=1 in your shell.
    2 Use lsofnet, ports, sniff aliases/functions to identify suspicious connections.
    3 Capture relevant traffic with capture_http or narrowed scopes.
    4 Use SSH helpers to reach remote logs or jump hosts quickly.

    Mini-lab:

    • Simulate incoming malicious connection on port 4444.
    • Use alias and functions to detect, capture, and remote into logs via SSH.
    • Toggle INCIDENT_MODE and observe prompt changes.

    3. Custom Completion and Tab Autocompletion for Tools

    Understanding how to build or customise your own completions gives you speed and control.

    How-To:

    1. Enable Compinit Safely
    zsh
       autoload -Uz compinit
       compinit -u   # -u disables insecure file warnings
    
    1. Writing a Custom Completion for a Tool
    bash
       # For 'nmap' command completion of scripts
       # Save as _nmap in ~/.zsh/completion
       # WARNING: This snippet defines completions; misuse could confuse scripts. It's safe but ensure correct system paths.
       #compdef _nmap nmap
    
       _nmap() {
         local -a scripts
         scripts=(${(f)"$(ls /usr/share/nmap/scripts)"} )
         _arguments \
           '-p[ports to scan]' \
           'scripts:script files:_files -W /usr/share/nmap/scripts' \
           '*:target hosts:_hosts'
       }
    
    1. Loading Custom Completions
    zsh
       fpath=(~/.zsh/completion $fpath)
       compinit
    
    1. Fuzzy Completion and Z-Styles
    zsh
       zstyle ':completion:*' matcher-list 'm:{a-z}={A-Za-z}'
       zstyle ':completion:*' rehash true
    

    Actionable Takeaways:

    • You get to tab through your nmap scripts directory and pick with context.
    • Completion files live in fpath locations. Protect them and check ownership.
    • Always re-evaluate matcher-list and rehash behaviours for speed/security.

    Mini-lab:

    • Write a custom completion for ssh targets (based on reading ~/.ssh/config entries).
    • Test fuzzy matching: typing ssh serv finds ssh server-alpha.example.com.
    • Benchmark tab-complete speed before and after many completion files loaded.

    4. Debugging, Tracing, and Incident Investigation in Zsh

    Know your shell in the trenches when things go wrong.

    How-To:

    1. Enable XTRACE for Scripts
    bash
       # WARNING: Tracing scripts may expose secrets (passwords, keys). Use with care and scrub output.
       set -x
       # ... your commands
       set +x
    
    1. Using zsh -x or zsh -o verbose
      - Run entire script with zsh -x script.zsh to see expanded commands.
      - -o verbose prints each line before execution.

    2. Logging Shell Sessions

    zsh
       # In .zshrc
       if [[ -n "$SSH_CONNECTION" ]]; then
         exec script -q ~/session_logs/$(date +%Y%m%d_%H%M%S)_$$.log
       fi
    
    1. Function to Monitor Command Execution Time
    bash
       track_cmd() {
         local start end elapsed
         start=$(date +%s%N)
         "$@"
         end=$(date +%s%N)
         elapsed=$(( end - start ))
         echo "Command '$*' took $((elapsed/1000000)) ms"
       }
    

    Checklist:

    • [ ] Do you have tracing for suspect scripts?
    • [ ] Are session logs being captured (especially remote sessions)?
    • [ ] Can you measure lag or slow commands (I/O, network latency)?
    • [ ] Do you rotate or secure logs so attacker cannot overwrite them?

    Mini-lab:

    • Craft a shell script with intentional mistakes.
    • Run with zsh -x, reproduce logs.
    • Time certain operations (e.g. download test file, parse large log), compare with track_cmd.

    5. Advanced Shell Customisation: Prompt Themes, Safety, Context Awareness

    When you're past barebones, customise with awareness that every flashy prompt is another attack surface.

    How-To:

    1. Git and VCS Awareness but Safe
    zsh
       autoload -Uz vcs_info
       precmd() { vcs_info }
       zstyle ':vcs_info:*' formats '(%b)'
       PROMPT='%n@%m:%~ ${vcs_info_msg_0_} %# '
    
    1. Context – VPN, Root, Container Indicators
    zsh
       if command -v vpnstatus >/dev/null 2>&1 && vpnstatus | grep -q "Connected"; then
         PROMPT="%F{green}[VPN]%f $PROMPT"
       fi
    
       if [[ $EUID -eq 0 ]]; then
         PROMPT="%F{red}[ROOT]%f $PROMPT"
       fi
    
    1. Colour Safe Tokens Only
      - Avoid exposing credentials or secrets via coloured command outputs.
      - Use colours for roles (root, user), status (healthy, alert).

    2. Performance Tuning for Prompt
      - Use promptsubst sparingly; heavy commands in precmd slow down every prompt.
      - Cache expensive computations (e.g. git branch) in background jobs or via async functions.

    Actionable Takeaways:

    • Context-awareness helps avoid errors when you're root or inside VPN or dev/test.
    • Themes are beautiful but slow prompts kill productivity, open windows for timing attacks or side channels.
    • Every added function in your prompt must be trusted, owned, immutable.

    Mini-lab:

    • Build a prompt that changes colour when you sudo or when a VPN is connected.
    • Measure response time of prompt before and after adding Git status and VPN check.
    • Break read-only permissions on a prompt file and observe safety implications.

    Internal Mindset: Security-First Shell Culture

    • Assume all config is supply-chain exposed. Dotfiles, completion scripts, themes, treat them like code that could betray you.
    • Version control your shell config, but encrypt secrets. Git repos are fine, but do not commit private keys, tokens.
    • Audit regularly: run revssh or check for backdoors in your dotfiles. Know what’s being sourced or autoloaded.
    • Think in layers: even if the shell is compromised, steps above (kernel, user permissions, file immutability) must defend you.

    Mastering Zsh: Tips and Tricks for Power Users

    Aim

    This guide shows you how to improve your command-line productivity and shell customisation using Zsh. You will learn practical techniques to enhance navigation, alias creation, completion, prompt design and session management.

    Learning outcomes

    By completing this guide you will be able to:
    - Create and manage aliases and functions to speed up frequent tasks
    - Use tab completion and globbing to reduce typing and mistakes
    - Customise your prompt to show useful information
    - Use history and session tools to recall past commands efficiently
    - Employ plugin systems or frameworks to extend Zsh’s capabilities

    Prerequisites

    • A computer with Zsh installed (macOS, Linux or Windows Subsystem for Linux)
    • Basic knowledge of shell usage (cd, ls, basic file manipulation)
    • Access to edit configuration files (for example .zshrc or ~/.zshrc)
    • Optional: familiarity with a plugin manager such as Oh My Zsh or zplug

    Tips and Tricks

    1. Aliases and custom functions

    Define short commands to simplify common workflows:

    bash
    # In your .zshrc
    
    # Alias for listing files with human readable sizes
    alias ll='ls -lh'
    
    # Function to create a directory and move into it
    mkcd() {
      mkdir -p -- "$1" && cd -- "$1"
    }
    

    Load the changes by running:

    bash
    source ~/.zshrc
    

    2. Enhanced tab completion and globbing

    Enable more powerful completion and filename matching:

    bash
    # In your .zshrc
    
    autoload -U compinit
    compinit
    
    # Enable recursive globbing: **
    setopt globstar
    
    # Case insensitive matching in completions
    zstyle ':completion:*' matcher-list 'm:{a-z}={A-Z}'
    

    3. Custom prompt design

    Make your prompt carry helpful context:

    bash
    # In your .zshrc
    
    # Example prompt: user@host current_dir (git_branch) >
    autoload -U vcs_info
    precmd() { vcs_info }
    setopt prompt_subst
    PROMPT='%n@%m %~ ${vcs_info_msg_0_} %# '
    zstyle ':vcs_info:git:*' formats '(%b)'
    

    4. Command history and recall

    Leverage history settings to speed up recalling past commands:

    bash
    # In your .zshrc
    
    HISTFILE=~/.zsh_history
    HISTSIZE=10000
    SAVEHIST=10000
    
    # Shared history between sessions
    setopt share_history
    
    # Search history with Ctrl-R
    bindkey '^R' history-incremental-search-backward
    

    5. Session persistence and workspaces

    Keep your sessions organised and restore your environment:

    • Use tmux or screen to maintain sessions when you disconnect
    • For project-based environment settings consider direnv which loads environment variables per directory

    6. Plugin frameworks and useful plugins

    Extend Zsh without reinventing wheels:

    • Install a framework such as Oh My Zsh, zplug or antibody
    • Useful plugins:
    • git for repository status and branch completion
    • zsh-autosuggestions for suggesting commands as you type
    • zsh-syntax-highlighting for visible feedback to syntax errors

    Install example using zplug:

    bash
    # In your .zshrc
    
    if ! command -v zplug >/dev/null; then
      curl -sL https://github.com/zplug/zplug/raw/master/install.zsh | zsh
    fi
    
    zplug "zsh-users/zsh-autosuggestions"
    zplug "zsh-users/zsh-syntax-highlighting"
    
    # Install all plugins
    zplug install
    

    By applying these techniques you will make Zsh more efficient, personalised and powerful.

    You taste rain on the neon streets now. The network still pulses with flickering packets. Your prompt glows red and green, telling you you are root, VPN locked, in Exfiltration Mode. You just unset eval, hardened your PATH, logged every SSH caress and every alias that could kill your system if misused. You feel sharp edges: completion scripts, prompt colours, context awareness, debugging flags. And you know now , when the syslog blazes, when the IDS lights, when the code tries to slip past , you are ready. Your shell, your Zsh, is alive, a razor between you and the dark.