Shell basics.
Navigation
pwd # current dir
cd /etc # absolute
cd ../parent # relative
cd # home
cd - # previous
ls -la
ls -lah # human sizes
ls -lat # by time
ls -lhS # by size
Files
cp source dest
cp -r dir1 dir2 # recursive
cp -a dir1 dir2 # archive (preserves perms, links, time)
mv old new
rm file
rm -rf dir # CAREFUL
ln -s target link # symlink
ln target hardlink # hard link
touch file # create / update timestamp
mkdir -p a/b/c # create with parents
rmdir empty_dir # only if empty
Reading
cat file
less file # paged; q to quit, /search, n next
head -n 20 file
tail -n 20 file
tail -f file # follow
wc -l file # lines
file foo # type
stat foo
Redirects
cmd > file # stdout to file (overwrite)
cmd >> file # append
cmd 2> err # stderr
cmd > out 2>&1 # both
cmd &> all # both (bash shortcut)
cmd < input # stdin from file
cmd <<< "str" # here string
cmd <<EOF # here doc
line1
line2
EOF
Pipes
cmd1 | cmd2 # stdout to stdin
cmd1 |& cmd2 # both (stdout + stderr)
ls | grep foo
ps aux | grep nginx | grep -v grep
tee
cmd | tee file # print + save
cmd | tee -a file # append
cmd | tee >(grep err) # process substitution
Background / jobs
cmd & # background
jobs # list
fg %1 # foreground job 1
bg %1
kill %1
disown %1 # detach from shell
nohup cmd & # survive shell exit
history
history
!42 # run command 42
!! # run last
!ssh # last starting with ssh
^old^new # quick fix
ctrl-r # reverse search
Quoting
echo 'literal $foo' # no expansion
echo "expand $foo" # variable expansion
echo `cmd` # command substitution (old)
echo $(cmd) # command substitution (preferred)
Variables
NAME=value # no spaces around =
export NAME=value # exported to subprocesses
echo $NAME
echo ${NAME}_suffix
echo ${NAME:-default} # default if unset
echo ${NAME:?error msg} # error if unset
unset NAME
Globs
ls *.txt
ls a?.txt # ? = one char
ls a[1-3].txt
ls **/*.txt # recursive (shopt -s globstar)
Brace expansion
echo {1..10} # 1 2 3 ... 10
echo {a,b,c}.txt # a.txt b.txt c.txt
mkdir -p /var/{log,run,tmp}
cp file{,.bak} # cp file file.bak
Loops
for f in *.txt; do
echo $f
done
while read line; do
echo $line
done < file
until [ -f /done ]; do
sleep 1
done
Conditionals
if [ -f file ]; then echo exists; fi
if [[ $x -gt 10 ]]; then echo big; fi
[ -f file ] # file exists
[ -d dir ] # directory
[ -e path ] # exists
[ -r file ] # readable
[ -w file ] # writable
[ -x file ] # executable
[ -s file ] # non-empty
[ -z "$str" ] # empty
[ -n "$str" ] # non-empty
[ "$a" = "$b" ]
[ "$a" != "$b" ]
[ "$a" -eq "$b" ] # numeric ==
[ "$a" -lt "$b" ]
case
case "$x" in
a|A) echo letter A;;
[0-9]) echo digit;;
*) echo other;;
esac
Functions
greet() {
echo "Hi $1"
return 0
}
greet "World"
Aliases
alias ll='ls -lah'
alias gst='git status'
In ~/.bashrc / ~/.zshrc.
set -e / set -u / set -o pipefail
#!/bin/bash
set -euo pipefail
-e: exit on error.-u: error on undefined var.-o pipefail: pipe fails if any stage fails.
Recommended for scripts.
Exit codes
cmd; echo $? # 0 = success
true; echo $? # 0
false; echo $? # 1
ssh / scp
ssh user@host
ssh -i ~/.ssh/key user@host
ssh -p 2222 user@host
ssh user@host 'command'
scp file user@host:/path/
scp -r dir user@host:/path/
tar / zip
tar czf out.tar.gz dir/
tar xzf out.tar.gz
tar tzf out.tar.gz # list
tar cJf out.tar.xz dir/ # xz (better compression)
tar cf - dir | gzip > out.tar.gz # streamed
zip -r out.zip dir/
unzip out.zip
Common mistakes
rm -rf $VAR/with empty$VAR→ wipes root.- Missing quotes around
$var→ splits on whitespace. [ -e "$x" ]vs[[ -e $x ]]—[[]]is bash-only but safer.- Using
$1, $2outside script — those are positional args. - Spaces around
=in assignment → syntax error.
Read this next
If you want my shell setup (zsh, starship, fzf), it’s at rajpoot.dev .
Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .