Practical Linux Tricks for the Aspiring Hacker
A curated list of fanciful Linux tricks I use to bolster my command-line prowess and activate Sage Mode.
This is a collection of commands I've picked up over the last few years, which I've found immensely useful. My favourite ones are probably:
less
: search/filter on a file or long text^r
: reverse search!$
: last argument of previous command
By "favourite", I mean I've used these commands a lot, and they've drastically increased my productivity.
Cool Stuff
Control (^
) Commands
^c # Duh. https://xkcd.com/416/
^d # Exit / EOF.
^r # Reverse search: for those long commands stashed in history.
Ternary Expression
[ 1 -eq 1 ] && echo 'true' || echo 'false'
# true
Clear screen. Useful for graphical hiccups.
reset
Run shell script without chmod +x
.
. ~/.zshrc # Dot command.
source ~/.zshrc
Tree view of files.
tree
Strings
Double-Quotes vs. Single-Quotes
- Double-quotes allow variable expansion and command substitution.
- Single-quotes don't. Prefer single-quotes for simple strings.
echo "$((1+1))" "$SHELL"
# 2 /bin/zsh
echo '$((1+1))' '$SHELL'
# $((1+1)) $SHELL
Multi-Line / Escape
Prefix the string with $
.
echo $'...'
Escape Single-Quotes
Example
Multi-Line Strings.
echo $'1\n2\n3'
# 1
# 2
# 3
Find words containing 't
in comma-separated line.
echo -n $'can\'t,don\'t,I\'ll,I\'m,won\'t' | awk -vRS=, $'$0 ~ /\'t/'
# can't
# don't
# won't
Previous-Command Tricks
$?
: exit code of previous command- By convention, 0 means no error. Non-0 implies an error occurred.
!!
: previous command!$
or$_
: last argument of previous command
Examples
Retry with sudo.
mkdir /var/tmp/lol
# Permission denied.
sudo !!
# Yay!
Found an interesting directory, but forgot to cd.
ls long/path
cd !$
# → cd long/path
Rename file in folder from file.txt to booyah.md.
cat long/path/file.txt
mv "!$" "$(dirname !$)/booyah.md"
# → mv long/path/file.txt long/path/booyah.md
Other Useful Commands (stolen from here)
!!:n
- nth argument from previous command!^
- first argument (after the program/built-in/script) from previous command!*
- all arguments from previous command!n
- command numbern
fromhistory
!pattern
- most recent command matchingpattern
!!:s/find/replace
- last command, substitutefind
withreplace
Redirection
< out.txt # Read from file (to stdin).
> out.txt # Write to file (from stdout).
>> out.txt # Append to file (from stdout).
2> out.txt # Write to file (from stderr).
&> out.txt # Redirect all output.
&> /dev/null # Redirect everything into the void.
2>&1 # Redirect stderr to stdout.
>& # Same as `&>`.
Powerful Utilities
awk
: filter lines, filter columns, math, scripting, etc.sed
: filter/replace textgrep
: filter linescut
: filter columnstr
: replace/remove characterswc
: count characters/bytes/wordsfind
: find files in folder, execute command for each file with-exec
xargs
: feed arguments into commands, simple cmdline multi-processing
I won't cover too much of these commands here, as tons of articles already cover them. And you can browse examples online or in their man
pages.
awkward things
awk - Cut
awk '$0=$3' # Cut third field.
awk '{print $3}' # Print third field. (Pretty much same as the command above.)
# Use ',' as field delimiter, e.g. for CSVs.
awk -F, '{print $3}'
# or use the script variable `FS` (Field Separator).
awk -v FS=, '{print $3}
awk - Filtering
awk '$1 == 1' # Filter lines with first field = 1.
awk '$0 ~ /^foo/' # Filter lines with regex.
awk - Math
awk '{$1 += 5}1' # Add 5 to the first arg, then print the line.
seq 1 3 | awk '{$1 += 5}1'
# 6
# 7
# 8
awk - Scripting
awk '{print "booyah",$1,"yahoo"}'
# awk also has variables, if, for, while, arrays, etc.
Script variables. (Useful for configuring row/column delimiters.)
- RS: Record Separator (rows)
- FS: Field Separator (columns)
- ORS: Output Row Separator
- OFS: Output Field Separator
- NR: Record Number (current row, 1-indexed) [read-only]
- NF: Number of Fields [read-only]
grep
Useful Flags
# case-insensitive
-i
# regex
-E
# non-match (inVert)
-v
xargs
xargs is a versatile command-line utility that allows efficient execution of commands from, making it a powerful tool for automation and batch processing.
Interesting options:
-P <n> # max procs
-n <n> # num args
-I {} # pattern to insert into command
Examples
Combine multiple lines into 1 line.
echo $'1\n2\n3' | xargs
# Output: 1 2 3 (no newline)
Multi-Processing: Execute ./do-something-to-file.sh <file>
on multiple files, with at most 4 processes.
cat files.txt | xargs -P 4 -n1 ./do-something-to-file.sh
Multi-Processing: Port Scan with Ports 1-1000 Through proxychains
.
seq 1 1000 | xargs -P 50 -I{} proxychains4 -q nmap -p {} -sT -Pn --open -n -T4 --oN nmap.txt --append-output 192.168.101.10
Other Utilities
basename ~/.bashrc # .bashrc # Filename, without path.
dirname ~/.bashrc # /home/bob/ # Path to file.
Directory Stack
pushd # Push current directory, for easier returning.
popd # Return to directory on top of stack.
pushd
/popd
Example
cd ~/a/b/c
pushd deep/nested/directory
# Jump to `deep/nested/directory`, push `~/a/b/c` into the stack.
cd ../../jump/around/some/more
cd ../../and/a/little/more
popd # Return to `~/a/b/c`.
less
less
is a powerful text viewer (read-only), with capabilities to navigate, search, and filter lines in a file or long text.
less - Nice Options
less file.txt
# Renders ANSI colors.
less -R file.txt
# Pager search becomes case insensitive.
less -I file.txt
# Line numbers.
less -N file.txt
less - Navigation
j # Line down.
k # Line up.
f # Page down.
b # Page up.
d # Half-page down.
u # Half-page up.
g # Go to start of file.
G # Go to end of file.
<n> g # Go to nth line.
# Go to the n% line of the file.
<n> p
20p 40p 50p 80p
# What's the current line?
^g
less - Search / Filtering
# Search (regex enabled).
/ <pattern>
# For case-insensitive search, use `less -I`.
# Navigate search results: next/prev result.
nN
# Filter lines by search.
& <pattern>
# Filter NON-MATCHING lines
& ! <pattern>
# Clear filter.
& <enter>
less - Scrolling
Personally, I prefer less+F
over tail -f
.
Use ^c
to exit the feed.
# Continuous feed (e.g. for streams of data)
F
less - Working with Multiple Files
less
also works with multiple files passed in the command line, e.g. less *.txt
.
# Next file.
:n
# Previous file.
:p
More commands in man less
.
Processes
fg/bg - "I'll be back."
Shells allow you to move processes between the foreground (which accepts interactive input) and background (to run things which don't require input).
^z # Push process to background (and pause it).
bg # Start background process.
fg # Bring most recent background process into foreground.
fg 2 # Bring job 2 into foreground.
jobs # View background jobs.
^c # Good ol' ctrl-c stops the process in fg.
kill <pid> # Kill process with given process ID.
# Start a command in the background.
<cmd> &
Example
Start an HTTP server on port 8080.
python -m http.server 8080 &
# [1] 17999
The process is started in the background with job number 1, PID 17999.
To kill the process:
fg
^c
# or
kill 17999
Process ID (PID) and Job Number are two different things.
- PIDs apply to all users in the entire system, and are assigned by the kernel.
- Job Numbers apply to the current shell, and are numbered linearly from 1 onwards.
View Running Procs
ps aux
Combine with grep
/less
for filtered results.
Networking
Information
# IP
ifconfig
ifconfig tun0
# Open Ports/Sockets
netstat -anp
# -a: all sockets
# -n: numeric addresses
# -p: associated processes
Listen/Connect
# Initiate a connection.
nc 192.168.1.1 8080
# Listen for a connection.
nc -nlvp 4444
# Listen persistently (keep listening after disconnect).
nc -nklvp 4444
Download Files
wget
# Quick download.
wget <url>
# Save to specific location.
wget <url> -O filename.txt
# Download silently (no progress), and display in less.
wget <url> -s | less
curl
curl <url> -s | less
# Use jq to format JSON.
curl some.api.site/api/v1/users/ -s | jq | less
Upload Files
Server
python -m uploadserver
- By default,
uploadserver
starts a server at port 8000. - Get our IP from
ifconfig
.
Client
curl -F files=@file1.txt -F files=@file2.txt 192.168.45.179:8000/upload
git gud
Git commands for completeness.
# Make new branch.
git checkout -b <name>
# Checkout commits in tree before HEAD.
git checkout HEAD~1 # 1 commit before.
git checkout HEAD~10 # 10 commits before.
# Checkout commit from parent.
git checkout HEAD^ # 1 commit before (from parent 1, base).
git checkout HEAD^2 # 1 commit before (from parent 2, target).
# Store changes locally.
git stash
git stash pop
# Clean edited files.
git reset [--hard]
# --hard removes unstaged files.
# View changes.
git diff | less
git diff <file> # See change in specific file.
# Jump through commits (to find, say, the cause of a bug).
git bisect [start|reset|good|bad|skip]
git tree
# Command-line git tree from git log.
git log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold yellow)%d%C(reset)' --all
# More detailed git-tree
git log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%aD%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n'' %C(white)%s%C(reset) %C(dim white)- %an%C(reset)' --all
# Add them as git aliases in ~/.gitconfig or script aliases in ~/.bashrc.
# See https://stackoverflow.com/a/9074343/10239789.
Fun watch: So You Think You Know Git?
vim
Haha. Nope.
Not covering that here.
Hacky Hack Hack
Generate Bytes
Buffer overflow for fun and profit.
echo
echo -n '\x01\x02'
echo -n '\x41' | xxd
00000000: 41 A
perl (good for repetitive sequences)
perl -e 'print "\x41"x4 . "\x42\x43"' | xxd
00000000: 4141 4141 4243 AAAABC
I've mentioned this elsewhere, but I'll repeat it here: I don't recommend using Python 3 to generate strings on-the-fly, as its string/byte-string mechanics are unintuitive. Prefer perl
or echo
instead.
For example: python -c 'print("\xc0")'
prints \xc3\x80
(À) instead of \xc0
. Why? Because the Python string "\xc0"
is interpreted as U+00C0, which is \xc3\x80
in UTF-8.
assert '\xc0'.encode() == b'\xc3\x80'
Printing bytes in Python is difficult to do concisely.