Aliases and Environment Variables
Aliases make interactive commands shorter. Environment variables pass named values to programs. They both shape the shell experience, but they behave very differently. An alias is expanded by the current interactive shell before a command runs. An environment variable is inherited by child processes when the shell starts them.
This article assumes Bash on Ubuntu Server LTS. It builds on Command Syntax and Help Documentation, especially quoting, type, and PATH.
Alias, Shell Variable, Environment Variable
| Concept | Used by | Inherited by child processes? | Example |
|---|---|---|---|
| Alias | Current interactive Bash command line | No | alias ll='ls -lah' |
| Shell variable | Current shell | Not unless exported | project='demo api' |
| Environment variable | Shell and child processes | Yes | export EDITOR='nano' |
An exported variable is still a shell variable in the parent shell, but Bash marks it to be copied into the environment of future child processes. A child process receives a copy. It cannot edit the parent shell's environment after it has started.
Working with Aliases
List current aliases:
Ubuntu's default interactive Bash configuration may define ll; a minimal server may not.
Create aliases:
Expected output:
There must be no spaces around =. Quote the alias value so the shell stores it as one value at definition time.
Alias expansion is not a full parameter system. It works well for fixed command prefixes:
It is not the right tool when you need to put an argument in the middle of a command. Use a function for that:
This example is useful for a lab, but a production-quality function should handle missing arguments and errors explicitly.
Bypassing or Removing an Alias
Check all meanings of a name:
Bypass an alias:
command ls bypasses shell functions as well as aliases and then uses the normal built-in or PATH lookup. A leading backslash prevents alias expansion for that word.
Remove one alias:
Avoid unalias -a unless you intentionally want to clear every alias in the current shell.
Shell Variables
Create a shell variable:
Output:
Assignment syntax is strict:
This is an assignment. But:
is parsed as a command named project with arguments = and demo.
Variable names are case-sensitive. By convention, variables intended for the environment are uppercase, while local shell variables in scripts are often lowercase.
Exporting to the Environment
A non-exported shell variable is not inherited:
Output:
Export it:
Output:
Create and export in one step:
Inspect variables:
The commands answer slightly different questions:
printenv EDITORprints the value from the process environment.echo "$EDITOR"prints the value after Bash expands the shell variable.declare -p EDITORis Bash-specific and shows attributes, including whether the variable is exported.
Remove a variable from the current shell:
This does not change processes that were already started.
Environment Inheritance
Environment is copied from parent to child when a new program starts:
Bash with APP_MODE=staging exported
-> python app.py receives APP_MODE=staging
-> bash receives APP_MODE=staging
-> env prints that child's environment
A child can change its copy without changing the parent:
export APP_MODE='staging'
bash -c 'APP_MODE=testing; echo "child=$APP_MODE"'
echo "parent=$APP_MODE"
Output:
Set a variable for one command only:
This sets LC_ALL for that sort process and its children, not for the current shell after the command exits.
Common Environment Variables
Inspect a few common values:
Important variables include:
HOME: the user's home directory.USERorLOGNAME: login username, depending on the program.SHELL: the user's configured login shell. It does not prove the currently running shell.PATH: command search path.LANGandLC_*: locale settings for language, sorting, and formatting.TZ: time zone used by some programs for that process.EDITORandVISUAL: preferred text editor for programs that honor them.
Check the current shell separately:
EDITOR and VISUAL
Some tools open an editor based on VISUAL or EDITOR:
Not every program honors these variables. Check the ENVIRONMENT section of that program's manual page.
TZ, LANG, and Reproducible Output
Compare time output:
The first command uses UTC only for that process. It does not change the system timezone.
Locale can change sort order and messages. Scripts that need bytewise, predictable sorting often use:
PATH: Command Search Path
Print PATH:
It is a colon-separated list. Bash searches left to right for an executable when the command name contains no /.
Find what will run:
Add a personal bin directory without losing the existing path:
Verify:
Warning
Do not replace PATH accidentally with export PATH="$HOME/.local/bin". That removes system directories and can make normal commands unavailable. Do not add . or world-writable directories to PATH, especially for root.
If Bash has cached command locations and you changed PATH, clear the hash table:
Persisting Settings
Aliases and variables typed at a prompt disappear when the shell exits. Persist them in the correct startup file.
For Bash on Debian and Ubuntu:
- Interactive non-login shells normally read
/etc/bash.bashrcand~/.bashrc. - Interactive login shells read
/etc/profile, then the first readable file among~/.bash_profile,~/.bash_login, and~/.profile. - Ubuntu's default
~/.profilemay source~/.bashrcfor Bash, but check the file instead of assuming. - Non-interactive shells, cron jobs, and
systemdservices do not automatically behave like your terminal.
Safely add an alias to ~/.bashrc:
Add this line near other aliases:
bash -n checks syntax without executing the file. Test in a new shell:
You can load the changed file into the current shell with:
Only source files you trust. Sourcing executes shell code in your current shell.
For environment variables used by services, do not rely on ~/.bashrc. Use the service manager's environment mechanism, such as a systemd unit Environment= line or an EnvironmentFile=, when that topic is covered.
Security and Production Notes
- Environment variables are visible to the process and often to diagnostic tools run by the same user or by root. Do not treat them as a secret store.
- Be careful with
LD_PRELOAD,LD_LIBRARY_PATH,PYTHONPATH, and similar variables. They can change what code a program loads. sudocommonly resets or filters environment variables according to policy. Do not assume your shell environment survives privilege escalation.- Keep aliases modest on shared servers. If your workflow depends on many personal aliases, you may make mistakes on a clean system.
- Prefer explicit commands in scripts. Aliases are mainly for interactive use.
Why LD_PRELOAD Actually Matters
LD_PRELOAD is an environment variable read by the dynamic linker, not by Bash. When a dynamically linked program starts, the linker loads every shared library named in LD_PRELOAD before the program's normal libraries, and functions in that library take priority over the same-named functions the program would otherwise call. This is a legitimate debugging mechanism — it is how tools like strace-adjacent profilers and memory checkers such as libasan inject themselves without recompiling the target program.
The same mechanism is what makes it a production risk. A malicious or compromised shared library set via LD_PRELOAD can override common libc functions — read, open, getuid, functions used for password checks — and change what a program observes or does, invisibly to the program itself:
printf '%s\n' 'int getuid(void) { return 0; }' > /tmp/fake.c
gcc -shared -fPIC -o /tmp/fake.so /tmp/fake.c
LD_PRELOAD=/tmp/fake.so id -u
id -u normally reports the real numeric user ID. Here it reports 0 for a non-root user, because LD_PRELOAD replaced the getuid call the program relies on to identify who is running it. No file permission was bypassed and no exploit was needed — the program's own libc calls were quietly redirected before it ever ran. This is why sudo and other privilege-aware tools sanitize LD_PRELOAD and related variables out of the environment before executing anything: an inherited LD_PRELOAD combined with elevated privilege turns a debugging feature into a way to hijack a privileged process's view of the system. Never set LD_PRELOAD, LD_LIBRARY_PATH, or accept them from an untrusted environment for anything that runs with elevated privilege, and treat an unexplained LD_PRELOAD in a service's environment as an incident to investigate, not a quirky configuration.
Practical Scenario: A Script Works in Your Terminal but Fails in Cron
Problem: a maintenance script runs cleanly when you execute it by hand over SSH, but the same script, scheduled with cron, fails or behaves differently.
Reproduce the cron environment instead of guessing:
Cron runs this line through /bin/sh (not necessarily your login Bash) as a non-interactive, non-login shell, which — as covered in How Shell and Bash Work — does not read ~/.bashrc. Confirm the difference directly by dumping both environments and comparing:
Add a one-shot cron job that dumps its own environment, wait for it to fire, then compare:
* * * * * /usr/bin/env > /home/admin/env-cron.txt
diff /home/admin/env-interactive.txt /home/admin/env-cron.txt
Typical output shows a much shorter PATH under cron, and the absence of any alias-dependent or ~/.bashrc-exported variable your script silently relied on:
< PATH=/home/admin/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
---
> PATH=/usr/bin:/bin
< APP_MODE=staging
Conclusion: do not fix this by copying ~/.bashrc logic into cron. Fix the script itself: use absolute paths for every command and file it touches, set any required variable explicitly at the top of the script or in the crontab's own environment lines (PATH=... and VAR=value above the schedule line), and do not assume a cron job's shell reads the same startup files your terminal does. Remove the one-shot debug line from the crontab once you have finished comparing.
Practical Scenario: Make a Safer Interactive Listing
Problem: you want a convenient ll command in your own terminal, but you do not want scripts or other users to depend on it.
Create it for the current shell:
Persist it in ~/.bashrc after backup:
Open a clean interactive Bash:
Conclusion: the alias is an interactive convenience. When writing documentation, scripts, or runbooks for other servers, write the full command:
Common Mistakes
- Putting spaces around
=in assignments. - Expecting aliases to work in scripts.
- Forgetting to quote variable values with spaces.
- Using
$SHELLas proof of the current process. - Overwriting
PATHinstead of prepending or appending. - Storing secrets in environment variables without understanding exposure.
- Editing
~/.bashrcwithout a backup or syntax check.
Exercises
- Create an alias
lh='ls -lh', test it withtype, then remove it withunalias. - Create a shell variable with a space in the value. Prove that a child Bash cannot see it until you export it.
- Temporarily run
datewithTZ=UTC, then prove your parent shell'sTZdid not change. - Add
$HOME/.local/bintoPATHin the current shell and verify its position without losing the originalPATH.
Verification criterion: you should be able to explain whether a setting affects only the current command line, the current shell, child processes, or future login sessions.
References
- Bash manual page: https://man7.org/linux/man-pages/man1/bash.1.html
- GNU Bash Reference Manual: https://www.gnu.org/software/bash/manual/bash.html
environ(7)Linux manual page: https://man7.org/linux/man-pages/man7/environ.7.htmlexecve(2)Linux manual page: https://man7.org/linux/man-pages/man2/execve.2.html- GNU Coreutils
printenv: https://www.gnu.org/software/coreutils/manual/html_node/printenv-invocation.html ld.so(8)Linux manual page (LD_PRELOAD,LD_LIBRARY_PATH): https://man7.org/linux/man-pages/man8/ld.so.8.htmlcrontab(5)Linux manual page: https://man7.org/linux/man-pages/man5/crontab.5.html