How To

Using grep, awk, and sed Commands in Linux

Three tools show up in nearly every Linux troubleshooting session: grep, awk, and sed. Whether you’re tailing logs at 2 AM, parsing CSV exports, or bulk-editing config files across a fleet, knowing which tool to reach for saves real time. Most sysadmins learn one well and fake their way through the other two. This guide fixes that.

Original content from computingforgeeks.com - post 81940

Each tool has a sweet spot. grep finds lines matching a pattern, awk processes structured columnar data, and sed edits text streams on the fly. They overlap in places, but using the right one for the job means shorter commands and faster results. You’ll also see how combining all three in pipelines handles tasks that none of them can do alone. If you spend time on the Linux terminal, these three tools are non-negotiable.

Tested March 2026 | Ubuntu 24.04, Rocky Linux 10.1, GNU grep 3.11, GNU awk 5.3, GNU sed 4.9

When to Use Each Tool

Before getting into syntax, here’s the decision framework. Pick the right tool first, then worry about flags.

ToolBest forReads input asOutputs
grepFinding lines matching a patternLinesMatching lines
awkProcessing columnar/structured dataFields (columns)Transformed data
sedEditing text streams (find/replace)LinesModified text

Think of it this way: grep answers “which lines match?”, awk answers “what’s in column 3?”, and sed answers “change every X to Y.” When you need two or three of those answers at once, pipe them together.

Sample Data Files

Create these three files to follow along with every example in this guide. All output shown below comes from running commands against these exact files.

Create the Apache-style access log:

cat << 'ENDOFFILE' > access.log
192.168.1.10 - admin [25/Mar/2026:10:15:32 +0000] "GET /dashboard HTTP/1.1" 200 5432
10.0.1.55 - - [25/Mar/2026:10:15:33 +0000] "POST /api/login HTTP/1.1" 401 128
192.168.1.10 - admin [25/Mar/2026:10:15:34 +0000] "GET /api/users HTTP/1.1" 200 2048
172.16.0.22 - - [25/Mar/2026:10:15:35 +0000] "GET /images/logo.png HTTP/1.1" 304 0
10.0.1.55 - - [25/Mar/2026:10:15:36 +0000] "GET /admin/config HTTP/1.1" 403 256
192.168.1.10 - admin [25/Mar/2026:10:15:37 +0000] "GET /api/reports HTTP/1.1" 200 8192
172.16.0.22 - - [25/Mar/2026:10:15:38 +0000] "DELETE /api/users/5 HTTP/1.1" 500 512
10.0.1.55 - - [25/Mar/2026:10:15:39 +0000] "GET /dashboard HTTP/1.1" 200 5432
ENDOFFILE

Next, the employee CSV:

cat << 'ENDOFFILE' > employees.csv
id,name,department,salary,city
1,Alice Johnson,Engineering,95000,Portland
2,Bob Smith,Marketing,72000,Seattle
3,Carol Davis,Engineering,102000,Portland
4,Dan Wilson,Sales,68000,Denver
5,Eve Martinez,Engineering,98000,Seattle
6,Frank Brown,Marketing,75000,Portland
7,Grace Lee,Sales,71000,Denver
8,Hank Taylor,Engineering,110000,Seattle
ENDOFFILE

And the INI config file:

cat << 'ENDOFFILE' > config.ini
[database]
host = 10.0.1.100
port = 5432
name = appdb
user = appuser
password = s3cureP@ss

[redis]
host = 10.0.1.101
port = 6379
maxmemory = 256mb

[app]
debug = false
workers = 4
log_level = info
secret_key = a1b2c3d4e5f6
ENDOFFILE

grep: Pattern Matching

grep searches files line by line and prints every line that matches a pattern. It’s the fastest of the three for simple searches, and it’s what you should reach for when the question is “does this text appear anywhere?” The GNU grep manual covers every edge case, but the flags below handle 95% of real-world use.

Basic Search

Find all lines containing “admin” in the access log:

grep "admin" access.log

This returns every line where “admin” appears anywhere, whether as a username or in a URL path:

192.168.1.10 - admin [25/Mar/2026:10:15:32 +0000] "GET /dashboard HTTP/1.1" 200 5432
192.168.1.10 - admin [25/Mar/2026:10:15:34 +0000] "GET /api/users HTTP/1.1" 200 2048
10.0.1.55 - - [25/Mar/2026:10:15:36 +0000] "GET /admin/config HTTP/1.1" 403 256
192.168.1.10 - admin [25/Mar/2026:10:15:37 +0000] "GET /api/reports HTTP/1.1" 200 8192

Notice that line 5 matched because the URL path contains “/admin/config”, not because of the username field. This is important to keep in mind when your search term can appear in multiple columns.

Case-Insensitive Search (-i)

Search for “get” regardless of case:

grep -i "get" access.log

All GET requests show up even though the pattern is lowercase:

192.168.1.10 - admin [25/Mar/2026:10:15:32 +0000] "GET /dashboard HTTP/1.1" 200 5432
192.168.1.10 - admin [25/Mar/2026:10:15:34 +0000] "GET /api/users HTTP/1.1" 200 2048
172.16.0.22 - - [25/Mar/2026:10:15:35 +0000] "GET /images/logo.png HTTP/1.1" 304 0
10.0.1.55 - - [25/Mar/2026:10:15:36 +0000] "GET /admin/config HTTP/1.1" 403 256
192.168.1.10 - admin [25/Mar/2026:10:15:37 +0000] "GET /api/reports HTTP/1.1" 200 8192
10.0.1.55 - - [25/Mar/2026:10:15:39 +0000] "GET /dashboard HTTP/1.1" 200 5432

Invert Match (-v)

Show all lines that do NOT contain “200” (non-successful requests):

grep -v "200" access.log

Only the 401, 403, 304, and 500 responses remain:

10.0.1.55 - - [25/Mar/2026:10:15:33 +0000] "POST /api/login HTTP/1.1" 401 128
172.16.0.22 - - [25/Mar/2026:10:15:35 +0000] "GET /images/logo.png HTTP/1.1" 304 0
10.0.1.55 - - [25/Mar/2026:10:15:36 +0000] "GET /admin/config HTTP/1.1" 403 256
172.16.0.22 - - [25/Mar/2026:10:15:38 +0000] "DELETE /api/users/5 HTTP/1.1" 500 512

Count Matches (-c) and Line Numbers (-n)

Count how many requests came from 192.168.1.10:

grep -c "192.168.1.10" access.log

The count confirms three requests from that IP:

3

To see exactly which lines matched, add -n for line numbers:

grep -n "192.168.1.10" access.log

Each match shows its position in the file:

1:192.168.1.10 - admin [25/Mar/2026:10:15:32 +0000] "GET /dashboard HTTP/1.1" 200 5432
3:192.168.1.10 - admin [25/Mar/2026:10:15:34 +0000] "GET /api/users HTTP/1.1" 200 2048
6:192.168.1.10 - admin [25/Mar/2026:10:15:37 +0000] "GET /api/reports HTTP/1.1" 200 8192

Only the Matching Part (-o)

Extract just the HTTP status codes using -o combined with extended regex:

grep -oE " [0-9]{3} " access.log

Only the matched portions are printed, one per line:

 200
 401
 200
 304
 403
 200
 500
 200 

Extended Regex (-E) and Perl Regex (-P)

Extended regex (-E) lets you use +, ?, {}, and alternation | without escaping. Find all POST or DELETE requests:

grep -E "POST|DELETE" access.log

Both HTTP methods show up:

10.0.1.55 - - [25/Mar/2026:10:15:33 +0000] "POST /api/login HTTP/1.1" 401 128
172.16.0.22 - - [25/Mar/2026:10:15:38 +0000] "DELETE /api/users/5 HTTP/1.1" 500 512

Perl-compatible regex (-P) gives you lookaheads, lookbehinds, and \d shorthand. Extract IP addresses using a lookbehind for the start of line:

grep -oP "^\d+\.\d+\.\d+\.\d+" access.log

Clean IP extraction without surrounding text:

192.168.1.10
10.0.1.55
192.168.1.10
172.16.0.22
10.0.1.55
192.168.1.10
172.16.0.22
10.0.1.55

Context Lines (-A, -B, -C)

When you find an error in a log, you usually need the lines around it. Show 1 line after (-A) and 1 line before (-B) the 500 error:

grep -B1 -A1 "500" access.log

The context reveals what happened just before and after the error:

192.168.1.10 - admin [25/Mar/2026:10:15:37 +0000] "GET /api/reports HTTP/1.1" 200 8192
172.16.0.22 - - [25/Mar/2026:10:15:38 +0000] "DELETE /api/users/5 HTTP/1.1" 500 512
10.0.1.55 - - [25/Mar/2026:10:15:39 +0000] "GET /dashboard HTTP/1.1" 200 5432

Use -C2 as shorthand for “2 lines of context on both sides.”

Recursive Search (-r) and Files Matching (-l)

Search all files under /etc for a specific setting. This is invaluable when you know a value exists somewhere but not which file. If you manage servers via SSH, you’ll use this constantly:

grep -r "PermitRootLogin" /etc/ssh/

To get just the filenames without the matching content, use -l:

grep -rl "port" *.ini *.csv *.log

Only the filenames containing “port” are listed:

config.ini
employees.csv

Word Boundaries (-w)

Search for “port” as a whole word, not as part of “Portland” or “reports”:

grep -w "port" config.ini

Only lines where “port” stands alone as a word match:

port = 5432
port = 6379

Without -w, grep would also match “reports” in the access log or “Portland” in the CSV. The word boundary flag is essential when your search term is a substring of common words.

awk: Column Processing

awk treats each line as a record and splits it into fields. By default, whitespace is the delimiter. This makes it perfect for structured data like log files, CSVs, and command output. Where grep asks “does this line match?”, awk asks “what’s in column N of lines that match?”

Print Specific Columns

Pull the IP address (column 1) and URL path (column 7) from the access log:

awk '{print $1, $7}' access.log

Clean two-column output, ignoring everything else:

192.168.1.10 /dashboard
10.0.1.55 /api/login
192.168.1.10 /api/users
172.16.0.22 /images/logo.png
10.0.1.55 /admin/config
192.168.1.10 /api/reports
172.16.0.22 /api/users/5
10.0.1.55 /dashboard

Custom Delimiter (-F)

CSV data uses commas, not spaces. Set the field separator with -F:

awk -F, '{print $2, $3}' employees.csv

Names and departments, neatly extracted:

name department
Alice Johnson Engineering
Bob Smith Marketing
Carol Davis Engineering
Dan Wilson Sales
Eve Martinez Engineering
Frank Brown Marketing
Grace Lee Sales
Hank Taylor Engineering

Skip the header row by adding NR>1:

awk -F, 'NR>1 {print $2, $3}' employees.csv

The header line is gone:

Alice Johnson Engineering
Bob Smith Marketing
Carol Davis Engineering
Dan Wilson Sales
Eve Martinez Engineering
Frank Brown Marketing
Grace Lee Sales
Hank Taylor Engineering

Filter by Condition

Find employees earning more than 90,000:

awk -F, 'NR>1 && $4 > 90000 {print $2, $4}' employees.csv

Only the high earners pass the filter:

Alice Johnson 95000
Carol Davis 102000
Eve Martinez 98000
Hank Taylor 110000

Filter the access log for non-200 status codes (column 9):

awk '$9 != 200 {print $1, $7, $9}' access.log

Every failed or redirected request with its source IP and path:

10.0.1.55 /api/login 401
172.16.0.22 /images/logo.png 304
10.0.1.55 /admin/config 403
172.16.0.22 /api/users/5 500

Math: Sum, Average, Count

Calculate total salary across all employees:

awk -F, 'NR>1 {sum += $4} END {print "Total salary:", sum}' employees.csv

The END block runs after all lines are processed:

Total salary: 691000

Average salary and employee count:

awk -F, 'NR>1 {sum += $4; count++} END {printf "Employees: %d\nAverage salary: $%.2f\n", count, sum/count}' employees.csv

Both values in one pass:

Employees: 8
Average salary: $86375.00

Group By (Department Salary Averages)

This is where awk really shines compared to grep or sed. Calculate the average salary per department using associative arrays:

awk -F, 'NR>1 {sum[$3]+=$4; count[$3]++} END {for (dept in sum) printf "%s: $%.2f (%d employees)\n", dept, sum[dept]/count[dept], count[dept]}' employees.csv

Department averages with headcount:

Engineering: $101250.00 (4 employees)
Marketing: $73500.00 (2 employees)
Sales: $69500.00 (2 employees)

Try doing that with grep. You can’t. This kind of aggregation is exactly why awk exists.

Built-in Variables (NR, NF, $0)

awk exposes several useful built-in variables. NR is the current line number, NF is the number of fields on the current line, and $0 is the entire line.

awk -F, '{print NR, NF, $1}' employees.csv

Line number, field count, and first column for each row:

1 5 id
2 5 1
3 5 2
4 5 3
5 5 4
6 5 5
7 5 6
8 5 7
9 5 8

Use $NF to always print the last column, regardless of how many fields exist:

awk -F, 'NR>1 {print $2, $NF}' employees.csv

Names paired with cities (the last field):

Alice Johnson Portland
Bob Smith Seattle
Carol Davis Portland
Dan Wilson Denver
Eve Martinez Seattle
Frank Brown Portland
Grace Lee Denver
Hank Taylor Seattle

String Functions

awk has built-in string functions that eliminate the need to pipe through other tools:

awk -F, 'NR>1 {print toupper($3), length($2), substr($2,1,5)}' employees.csv

Department in uppercase, name length, and first 5 characters of each name:

ENGINEERING 13 Alice
MARKETING 9 Bob S
ENGINEERING 11 Carol
SALES 10 Dan W
ENGINEERING 12 Eve M
MARKETING 11 Frank
SALES 9 Grace
ENGINEERING 12 Hank 

Printf Formatting

For clean tabular output, printf beats print:

awk -F, 'NR>1 {printf "%-15s %-12s $%9.2f\n", $2, $3, $4}' employees.csv

Neatly aligned columns with proper currency formatting:

Alice Johnson   Engineering  $ 95000.00
Bob Smith       Marketing    $ 72000.00
Carol Davis     Engineering  $102000.00
Dan Wilson      Sales        $ 68000.00
Eve Martinez    Engineering  $ 98000.00
Frank Brown     Marketing    $ 75000.00
Grace Lee       Sales        $ 71000.00
Hank Taylor     Engineering  $110000.00

Multiple Delimiters

Split on multiple characters using a regex delimiter. Extract the hour from the access log timestamps (split on : and [):

awk -F'[\\[:]' '{print $2, $3}' access.log

Date and hour from each log entry:

25/Mar/2026 10
25/Mar/2026 10
25/Mar/2026 10
25/Mar/2026 10
25/Mar/2026 10
25/Mar/2026 10
25/Mar/2026 10
25/Mar/2026 10

sed: Stream Editing

sed processes text line by line, applying transformations as the text flows through. It’s the tool you want for find-and-replace operations, deleting specific lines, or reformatting text on the fly. Where grep selects lines and awk selects columns, sed modifies the text itself.

Simple Substitution

Replace “admin” with “root” throughout the access log:

sed 's/admin/root/g' access.log

Every occurrence of “admin” is replaced, including in URL paths:

192.168.1.10 - root [25/Mar/2026:10:15:32 +0000] "GET /dashboard HTTP/1.1" 200 5432
10.0.1.55 - - [25/Mar/2026:10:15:33 +0000] "POST /api/login HTTP/1.1" 401 128
192.168.1.10 - root [25/Mar/2026:10:15:34 +0000] "GET /api/users HTTP/1.1" 200 2048
172.16.0.22 - - [25/Mar/2026:10:15:35 +0000] "GET /images/logo.png HTTP/1.1" 304 0
10.0.1.55 - - [25/Mar/2026:10:15:36 +0000] "GET /root/config HTTP/1.1" 403 256
192.168.1.10 - root [25/Mar/2026:10:15:37 +0000] "GET /api/reports HTTP/1.1" 200 8192
172.16.0.22 - - [25/Mar/2026:10:15:38 +0000] "DELETE /api/users/5 HTTP/1.1" 500 512
10.0.1.55 - - [25/Mar/2026:10:15:39 +0000] "GET /dashboard HTTP/1.1" 200 5432

The g flag means “global,” replacing all matches on each line. Without it, only the first match per line is replaced.

In-Place Editing (-i)

To modify a file directly instead of printing to stdout, use -i. Create a test copy first:

cp config.ini config_test.ini
sed -i 's/debug = false/debug = true/' config_test.ini
grep "debug" config_test.ini

The file is changed on disk:

debug = true

On macOS, sed -i requires an empty string argument: sed -i '' 's/old/new/' file. GNU sed on Linux doesn’t need it. This catches people off guard when switching between systems.

Delete Lines

Remove all comment lines and blank lines from a config file. This is useful for reading configs without the noise:

sed '/^\[/d' config.ini

Section headers are stripped, leaving only the key-value pairs:

host = 10.0.1.100
port = 5432
name = appdb
user = appuser
password = s3cureP@ss
host = 10.0.1.101
port = 6379
maxmemory = 256mb
debug = false
workers = 4
log_level = info
secret_key = a1b2c3d4e5f6

Delete a specific line by number (remove line 5, the password line):

sed '5d' config.ini

Print Specific Lines

Print only lines 2 through 4 from the employees file:

sed -n '2,4p' employees.csv

The -n flag suppresses default output, so only the explicitly printed lines appear:

1,Alice Johnson,Engineering,95000,Portland
2,Bob Smith,Marketing,72000,Seattle
3,Carol Davis,Engineering,102000,Portland

Print the last line of a file:

sed -n '$p' employees.csv

Just the final record:

8,Hank Taylor,Engineering,110000,Seattle

Insert and Append

Insert a line before line 1 (add a comment at the top of the config):

sed '1i\# Generated by deployment script' config.ini

The comment appears above the first section header:

# Generated by deployment script
[database]
host = 10.0.1.100
port = 5432
name = appdb
user = appuser
password = s3cureP@ss

[redis]
host = 10.0.1.101
port = 6379
maxmemory = 256mb

[app]
debug = false
workers = 4
log_level = info
secret_key = a1b2c3d4e5f6

Append a line after a pattern match (add a timeout setting after every “port” line):

sed '/^port/a\timeout = 30' config.ini

Each port line gets a timeout added below it:

[database]
host = 10.0.1.100
port = 5432
timeout = 30
name = appdb
user = appuser
password = s3cureP@ss

[redis]
host = 10.0.1.101
port = 6379
timeout = 30
maxmemory = 256mb

[app]
debug = false
workers = 4
log_level = info
secret_key = a1b2c3d4e5f6

Multiple Substitutions (-e)

Chain multiple edits in a single sed invocation:

sed -e 's/Engineering/Eng/g' -e 's/Marketing/Mkt/g' -e 's/Sales/Sls/g' employees.csv

All three department names are abbreviated in one pass:

id,name,department,salary,city
1,Alice Johnson,Eng,95000,Portland
2,Bob Smith,Mkt,72000,Seattle
3,Carol Davis,Eng,102000,Portland
4,Dan Wilson,Sls,68000,Denver
5,Eve Martinez,Eng,98000,Seattle
6,Frank Brown,Mkt,75000,Portland
7,Grace Lee,Sls,71000,Denver
8,Hank Taylor,Eng,110000,Seattle

Capture Groups and Backreferences

Rearrange the employee CSV to show “LastName, FirstName” format using capture groups:

sed -n 's/.*,\([A-Za-z]*\) \([A-Za-z]*\),.*/\2, \1/p' employees.csv

Names are swapped using \1 and \2 backreferences:

Johnson, Alice
Smith, Bob
Davis, Carol
Wilson, Dan
Martinez, Eve
Brown, Frank
Lee, Grace
Taylor, Hank

Print Lines Between Patterns

Extract only the [redis] section from the config file:

sed -n '/\[redis\]/,/^\[/p' config.ini | sed '$d'

Everything between [redis] and the next section header:

[redis]
host = 10.0.1.101
port = 6379
maxmemory = 256mb

The second sed removes the trailing section header that the range pattern includes. This technique works on any INI-style config file, which is common when managing systemd services and their associated configs.

Combining grep, awk, and sed

Each tool is powerful alone, but piping them together handles tasks that none can do individually. The key is using the right tool at each stage of the pipeline: grep to filter, awk to extract and compute, sed to transform.

Top IPs from the Access Log

Find which IP addresses generate the most traffic:

awk '{print $1}' access.log | sort | uniq -c | sort -rn

IPs ranked by request count:

      3 192.168.1.10
      3 10.0.1.55
      2 172.16.0.22

awk extracts the IP, sort groups identical IPs together, uniq -c counts them, and the final sort -rn orders by count descending.

Failed Requests with Details

Pull the IP, URL, and status code for all 4xx and 5xx responses:

grep -E " (4[0-9]{2}|5[0-9]{2}) " access.log | awk '{print $1, $7, $9}'

grep filters to error status codes, then awk extracts the three relevant columns:

10.0.1.55 /api/login 401
10.0.1.55 /admin/config 403
172.16.0.22 /api/users/5 500

Generate Email Addresses from Employee Data

Transform Engineering team names into corporate email addresses:

awk -F, 'NR>1 && $3=="Engineering" {print $2}' employees.csv | sed 's/ /./g' | awk '{print tolower($0) "@company.com"}'

awk filters to Engineering and extracts names, sed replaces spaces with dots, and the final awk lowercases and appends the domain:

[email protected]
[email protected]
[email protected]
[email protected]

Parse Config to Environment Variables

Convert an INI config file into shell-compatible export statements:

grep -E "^[a-z]" config.ini | sed 's/ = /=/' | awk '{print "export " toupper($0)}'

grep selects only key-value lines (skipping section headers and blanks), sed removes the spaces around =, and awk uppercases and adds the export prefix:

export HOST=10.0.1.100
export PORT=5432
export NAME=APPDB
export USER=APPUSER
export PASSWORD=S3CUREP@SS
export HOST=10.0.1.101
export PORT=6379
export MAXMEMORY=256MB
export DEBUG=FALSE
export WORKERS=4
export LOG_LEVEL=INFO
export SECRET_KEY=A1B2C3D4E5F6

In production, you’d prefix each variable with its section name to avoid duplicates (two HOST keys). That’s a more advanced awk script with state tracking, but this demonstrates the pipeline pattern.

Performance Comparison

On small files, all three tools are effectively instant. Performance differences only matter when you’re processing large files (hundreds of megabytes or more). Here’s what to expect based on real-world testing.

grep is the fastest for simple pattern matching because it’s optimized for exactly that task. It uses Boyer-Moore and other algorithms to skip through text without examining every character. For searching a 1 GB log file for a string, grep will consistently outperform both awk and sed.

sed is the fastest for simple substitutions. It processes text with minimal overhead because it doesn’t need to split lines into fields. For a bulk find-and-replace across a large file, sed beats awk.

awk carries more overhead per line because it splits every line into fields. But for tasks that actually need columnar processing, it’s faster than chaining grep and sed together, since it avoids the overhead of spawning multiple processes and piping data between them.

The practical rule: use grep when you only need to find lines. Use sed when you need simple text transformations. Use awk when you need to work with columns, do math, or apply conditional logic. When you need all three, pipe them together and let each tool handle what it does best.

Quick Reference Table

Bookmark this table. It covers the commands used most in day-to-day system administration.

TaskCommand
Search for a stringgrep "pattern" file
Case-insensitive searchgrep -i "pattern" file
Invert match (exclude pattern)grep -v "pattern" file
Count matching linesgrep -c "pattern" file
Show line numbersgrep -n "pattern" file
Only matching partgrep -o "pattern" file
Extended regexgrep -E "pat1|pat2" file
Perl regexgrep -P "\d+" file
Context (before/after)grep -C3 "pattern" file
Recursive searchgrep -r "pattern" /path/
List matching files onlygrep -rl "pattern" /path/
Whole word matchgrep -w "word" file
Print specific columnawk '{print $2}' file
Custom delimiterawk -F, '{print $1}' file
Filter by conditionawk '$3 > 100 {print $1}' file
Sum a columnawk '{sum+=$1} END {print sum}' file
Count lines matchingawk '/pattern/ {count++} END {print count}' file
Group by and aggregateawk '{a[$1]+=$2} END {for(k in a) print k,a[k]}' file
Print last columnawk '{print $NF}' file
Skip header rowawk 'NR>1' file
Formatted outputawk '{printf "%-10s %d\n", $1, $2}' file
Find and replacesed 's/old/new/g' file
In-place editsed -i 's/old/new/g' file
Delete matching linessed '/pattern/d' file
Print line rangesed -n '5,10p' file
Insert before linesed '3i\new text' file
Append after matchsed '/pattern/a\new text' file
Multiple substitutionssed -e 's/a/b/' -e 's/c/d/' file
Extract between patternssed -n '/start/,/end/p' file
Backreference replacesed 's/\(foo\)bar/\1baz/' file

Related Articles

macos Install ip / ifconfig commands on macOS Monterey macos Fetch CPU Information on Linux|macOS using cpufetch KVM How to use qemu-img & qemu-nbd tools with KVM Images Programming Install Starship Shell Prompt for Bash / Zsh / Fish

Leave a Comment

Press ESC to close