learn_bash_from_session_data 1.0.9 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +8 -1
- package/scripts/enrichment_builtins.py +1266 -0
- package/scripts/enrichment_coreutils.py +1499 -0
- package/scripts/enrichment_netproc.py +2270 -0
- package/scripts/enrichment_netsys.py +1601 -0
- package/scripts/enrichment_pkgcomp.py +2185 -0
- package/scripts/enrichment_textdev.py +2016 -0
- package/scripts/html_generator.py +154 -7
- package/scripts/knowledge_base.py +11521 -5626
- package/scripts/main.py +78 -34
- package/scripts/merge_enrichment.py +272 -0
- package/scripts/quiz_generator.py +69 -37
|
@@ -0,0 +1,1499 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Enrichment data for Coreutils, File Management, User/Group Administration,
|
|
3
|
+
Scheduling, Security, and Debugging commands.
|
|
4
|
+
|
|
5
|
+
This module provides supplemental fields (use_cases, gotchas, man_url, related,
|
|
6
|
+
difficulty, extra_flags) for thin entries in the COMMAND_DB knowledge base.
|
|
7
|
+
|
|
8
|
+
Sources consulted:
|
|
9
|
+
- man7.org Linux man-pages: https://man7.org/linux/man-pages/dir_section_1.html
|
|
10
|
+
- GNU Coreutils documentation: https://www.gnu.org/software/coreutils/manual/
|
|
11
|
+
- Official project documentation (git-scm.com, docs.docker.com, etc.)
|
|
12
|
+
- Greg's Wiki / BashPitfalls: https://mywiki.wooledge.org/BashPitfalls
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
ENRICHMENT_DATA = {
|
|
16
|
+
# =========================================================================
|
|
17
|
+
# TEXT PROCESSING & FILTERING
|
|
18
|
+
# =========================================================================
|
|
19
|
+
"awk": {
|
|
20
|
+
"man_url": "https://man7.org/linux/man-pages/man1/awk.1p.html",
|
|
21
|
+
"use_cases": [
|
|
22
|
+
"Extract and reformat columns from structured text: awk '{print $1, $3}' file.txt",
|
|
23
|
+
"Compute column sums or averages: awk '{sum+=$2} END{print sum}' data.csv",
|
|
24
|
+
"Filter lines matching a pattern: awk '/ERROR/ {print FILENAME, NR, $0}' *.log",
|
|
25
|
+
"Transform delimited data: awk -F',' '{print $2\": \"$4}' input.csv",
|
|
26
|
+
],
|
|
27
|
+
"gotchas": [
|
|
28
|
+
"Field separator -F is a regex, so -F'.' splits on every character -- use -F'\\.' for literal dots",
|
|
29
|
+
"Uninitialized variables default to 0 or empty string depending on context, which can mask bugs",
|
|
30
|
+
"POSIX awk and gawk differ significantly -- gawk extensions like FPAT and arrays-of-arrays are not portable",
|
|
31
|
+
],
|
|
32
|
+
"related": ["sed", "cut", "grep", "gawk"],
|
|
33
|
+
"difficulty": "intermediate",
|
|
34
|
+
"extra_flags": {
|
|
35
|
+
"-v": "Assign a variable before execution begins: awk -v OFS='\\t'",
|
|
36
|
+
"-F": "Set the input field separator (can be a regex)",
|
|
37
|
+
"-f": "Read the awk program from a file instead of the command line",
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
"cat": {
|
|
41
|
+
"man_url": "https://man7.org/linux/man-pages/man1/cat.1.html",
|
|
42
|
+
"use_cases": [
|
|
43
|
+
"Display file contents quickly: cat /etc/hostname",
|
|
44
|
+
"Concatenate multiple files: cat part1.txt part2.txt > combined.txt",
|
|
45
|
+
"Create small files with heredoc: cat <<'EOF' > config.yml",
|
|
46
|
+
],
|
|
47
|
+
"gotchas": [
|
|
48
|
+
"Useless use of cat: prefer redirection over 'cat file | cmd' -- use 'cmd < file' instead",
|
|
49
|
+
"No pagination for large files -- use less or more instead",
|
|
50
|
+
],
|
|
51
|
+
"related": ["less", "head", "tail", "tac"],
|
|
52
|
+
"difficulty": "beginner",
|
|
53
|
+
"extra_flags": {
|
|
54
|
+
"-A": "Show all non-printing characters including line endings ($) and tabs (^I)",
|
|
55
|
+
"-n": "Number all output lines",
|
|
56
|
+
"-s": "Squeeze multiple adjacent blank lines into one",
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
"cut": {
|
|
60
|
+
"man_url": "https://man7.org/linux/man-pages/man1/cut.1.html",
|
|
61
|
+
"use_cases": [
|
|
62
|
+
"Extract specific columns from CSV: cut -d',' -f1,3 data.csv",
|
|
63
|
+
"Pull usernames from /etc/passwd: cut -d: -f1 /etc/passwd",
|
|
64
|
+
"Extract character ranges: cut -c1-10 file.txt",
|
|
65
|
+
],
|
|
66
|
+
"gotchas": [
|
|
67
|
+
"cut cannot reorder fields -- cut -f3,1 outputs fields in file order (1 then 3), not 3 then 1",
|
|
68
|
+
"cut does not support multi-character delimiters -- use awk for that",
|
|
69
|
+
"Fields beyond the last delimiter are included in the last field, which can give unexpected results",
|
|
70
|
+
],
|
|
71
|
+
"related": ["awk", "paste", "tr", "sed"],
|
|
72
|
+
"difficulty": "beginner",
|
|
73
|
+
"extra_flags": {
|
|
74
|
+
"-d": "Set the field delimiter (default is TAB)",
|
|
75
|
+
"-f": "Select fields by number or range",
|
|
76
|
+
"--complement": "Invert the selection (print all fields except specified)",
|
|
77
|
+
"--output-delimiter": "Use a different delimiter for output",
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
"grep": {
|
|
81
|
+
"man_url": "https://man7.org/linux/man-pages/man1/grep.1.html",
|
|
82
|
+
"use_cases": [
|
|
83
|
+
"Search for patterns in files: grep -rn 'TODO' src/",
|
|
84
|
+
"Filter command output: ps aux | grep nginx",
|
|
85
|
+
"Find files containing a string: grep -rl 'api_key' /etc/",
|
|
86
|
+
"Count occurrences: grep -c 'error' application.log",
|
|
87
|
+
],
|
|
88
|
+
"gotchas": [
|
|
89
|
+
"grep pattern is BRE by default -- use -E for extended regex or -P for Perl regex",
|
|
90
|
+
"'grep pattern | grep -v grep' is a common antipattern -- use pgrep or brackets: grep '[n]ginx'",
|
|
91
|
+
"Binary file matches are suppressed by default -- use -a to force text mode or --binary-files=text",
|
|
92
|
+
],
|
|
93
|
+
"related": ["egrep", "fgrep", "sed", "awk"],
|
|
94
|
+
"difficulty": "beginner",
|
|
95
|
+
"extra_flags": {
|
|
96
|
+
"-P": "Use Perl-compatible regex (PCRE) for lookaheads and other advanced patterns",
|
|
97
|
+
"-o": "Print only the matching part of each line",
|
|
98
|
+
"-A": "Print N lines after each match",
|
|
99
|
+
"-B": "Print N lines before each match",
|
|
100
|
+
"-C": "Print N lines of context around each match",
|
|
101
|
+
"--include": "Search only files matching a glob pattern",
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
"egrep": {
|
|
105
|
+
"man_url": "https://man7.org/linux/man-pages/man1/grep.1.html",
|
|
106
|
+
"use_cases": [
|
|
107
|
+
"Search with extended regex without escaping: egrep '(error|warning|critical)' log.txt",
|
|
108
|
+
"Match complex patterns: egrep '^[0-9]{1,3}\\.' access.log",
|
|
109
|
+
"Filter with alternation: egrep 'GET|POST|PUT' access.log",
|
|
110
|
+
],
|
|
111
|
+
"gotchas": [
|
|
112
|
+
"egrep is deprecated -- use grep -E instead for forward compatibility",
|
|
113
|
+
"Some systems may remove egrep in future releases",
|
|
114
|
+
],
|
|
115
|
+
"related": ["grep", "fgrep", "sed", "awk"],
|
|
116
|
+
"difficulty": "beginner",
|
|
117
|
+
},
|
|
118
|
+
"fgrep": {
|
|
119
|
+
"man_url": "https://man7.org/linux/man-pages/man1/grep.1.html",
|
|
120
|
+
"use_cases": [
|
|
121
|
+
"Search for literal strings with special characters: fgrep 'price=$9.99' orders.txt",
|
|
122
|
+
"Fast multi-pattern search from a file: fgrep -f patterns.txt data.txt",
|
|
123
|
+
"Search for strings containing regex metacharacters without escaping",
|
|
124
|
+
],
|
|
125
|
+
"gotchas": [
|
|
126
|
+
"fgrep is deprecated -- use grep -F instead for forward compatibility",
|
|
127
|
+
"Cannot use any regex features -- patterns are strictly literal",
|
|
128
|
+
],
|
|
129
|
+
"related": ["grep", "egrep", "strings", "awk"],
|
|
130
|
+
"difficulty": "beginner",
|
|
131
|
+
},
|
|
132
|
+
"gawk": {
|
|
133
|
+
"man_url": "https://www.gnu.org/software/gawk/manual/gawk.html",
|
|
134
|
+
"use_cases": [
|
|
135
|
+
"Use GNU-specific features like FPAT for CSV parsing: gawk -v FPAT='[^,]*|\"[^\"]*\"'",
|
|
136
|
+
"Process multi-dimensional arrays: gawk '{a[$1][$2]+=$3}'",
|
|
137
|
+
"Use networking with /inet/tcp for simple socket programming",
|
|
138
|
+
"Profile awk scripts with --profile for performance analysis",
|
|
139
|
+
],
|
|
140
|
+
"gotchas": [
|
|
141
|
+
"gawk extensions are not portable to mawk or nawk -- avoid them in scripts that must run on BSD or macOS",
|
|
142
|
+
"The @include directive and @namespace are gawk-only features",
|
|
143
|
+
"gawk is significantly slower than mawk for simple text processing tasks",
|
|
144
|
+
],
|
|
145
|
+
"related": ["awk", "sed", "perl", "cut"],
|
|
146
|
+
"difficulty": "intermediate",
|
|
147
|
+
"extra_flags": {
|
|
148
|
+
"--csv": "Enable proper CSV parsing mode (gawk 5.3+)",
|
|
149
|
+
"--sandbox": "Disable I/O redirection and system() for safe execution",
|
|
150
|
+
"-i": "Include a source library (e.g., -i inplace for in-place editing)",
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
"head": {
|
|
154
|
+
"man_url": "https://man7.org/linux/man-pages/man1/head.1.html",
|
|
155
|
+
"use_cases": [
|
|
156
|
+
"Preview the first lines of a file: head -n 20 large_file.log",
|
|
157
|
+
"Display all but the last N lines: head -n -5 file.txt",
|
|
158
|
+
"Quickly check CSV headers: head -1 data.csv",
|
|
159
|
+
],
|
|
160
|
+
"gotchas": [
|
|
161
|
+
"head -n -N (exclude last N lines) requires reading the entire file, so it is slow on huge files",
|
|
162
|
+
"Default is 10 lines, which may not be enough to see the structure of a file",
|
|
163
|
+
],
|
|
164
|
+
"related": ["tail", "less", "cat", "sed"],
|
|
165
|
+
"difficulty": "beginner",
|
|
166
|
+
"extra_flags": {
|
|
167
|
+
"-c": "Print the first N bytes instead of lines",
|
|
168
|
+
"-q": "Suppress headers when printing multiple files",
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
"sed": {
|
|
172
|
+
"man_url": "https://man7.org/linux/man-pages/man1/sed.1.html",
|
|
173
|
+
"use_cases": [
|
|
174
|
+
"Find and replace text in files: sed -i 's/old/new/g' file.txt",
|
|
175
|
+
"Delete lines matching a pattern: sed '/^#/d' config.conf",
|
|
176
|
+
"Extract lines by range: sed -n '10,20p' file.txt",
|
|
177
|
+
"Insert text before or after a line: sed '/pattern/a\\new line' file.txt",
|
|
178
|
+
],
|
|
179
|
+
"gotchas": [
|
|
180
|
+
"sed -i behaves differently on macOS (BSD) vs Linux (GNU) -- macOS requires -i '' while GNU uses -i alone",
|
|
181
|
+
"The delimiter in s/// can be any character -- use s|old|new| when paths contain slashes",
|
|
182
|
+
"Greedy matching is the default and there is no non-greedy quantifier -- use [^/]* instead of .*",
|
|
183
|
+
],
|
|
184
|
+
"related": ["awk", "grep", "tr", "perl"],
|
|
185
|
+
"difficulty": "intermediate",
|
|
186
|
+
"extra_flags": {
|
|
187
|
+
"-E": "Use extended regular expressions (ERE) instead of basic (BRE)",
|
|
188
|
+
"-n": "Suppress automatic printing -- only print when explicitly told via p command",
|
|
189
|
+
"-z": "Use NUL as line separator for processing NUL-delimited data",
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
"sort": {
|
|
193
|
+
"man_url": "https://man7.org/linux/man-pages/man1/sort.1.html",
|
|
194
|
+
"use_cases": [
|
|
195
|
+
"Sort files alphabetically or numerically: sort -n numbers.txt",
|
|
196
|
+
"Sort by specific column: sort -t',' -k3,3n data.csv",
|
|
197
|
+
"Remove duplicates while sorting: sort -u names.txt",
|
|
198
|
+
"Sort human-readable sizes: du -sh * | sort -h",
|
|
199
|
+
],
|
|
200
|
+
"gotchas": [
|
|
201
|
+
"Locale affects sort order -- use LC_ALL=C for byte-order sorting and reproducible results",
|
|
202
|
+
"Numeric sort -n only works on leading numbers -- use -k to sort by a specific field",
|
|
203
|
+
"sort -u compares entire lines, not just the sort key -- use sort | uniq for key-based dedup",
|
|
204
|
+
],
|
|
205
|
+
"related": ["uniq", "cut", "awk", "tsort"],
|
|
206
|
+
"difficulty": "beginner",
|
|
207
|
+
"extra_flags": {
|
|
208
|
+
"-h": "Sort human-readable numbers like 2K, 1G",
|
|
209
|
+
"-V": "Version-number sort (e.g., 1.2 before 1.10)",
|
|
210
|
+
"-S": "Set the main-memory buffer size for large sorts",
|
|
211
|
+
"-t": "Set the field separator character",
|
|
212
|
+
"-k": "Sort by a specific key/field",
|
|
213
|
+
"-s": "Stabilize sort by disabling last-resort comparison",
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
"tail": {
|
|
217
|
+
"man_url": "https://man7.org/linux/man-pages/man1/tail.1.html",
|
|
218
|
+
"use_cases": [
|
|
219
|
+
"Follow a log file in real time: tail -f /var/log/syslog",
|
|
220
|
+
"Show the last N lines: tail -n 50 application.log",
|
|
221
|
+
"Skip the first N lines (start from line N+1): tail -n +2 data.csv",
|
|
222
|
+
"Follow multiple log files simultaneously: tail -f *.log",
|
|
223
|
+
],
|
|
224
|
+
"gotchas": [
|
|
225
|
+
"tail -f does not follow log rotation -- use tail -F (or tail --follow=name) to handle rotated files",
|
|
226
|
+
"tail -n +N starts from line N, not after N lines -- tail -n +1 prints the entire file",
|
|
227
|
+
"Piping tail -f can buffer output -- use --pid to stop when a process exits",
|
|
228
|
+
],
|
|
229
|
+
"related": ["head", "less", "cat", "multitail"],
|
|
230
|
+
"difficulty": "beginner",
|
|
231
|
+
"extra_flags": {
|
|
232
|
+
"-F": "Follow by name and retry if file is rotated or recreated",
|
|
233
|
+
"-c": "Output the last N bytes instead of lines",
|
|
234
|
+
"--pid": "Terminate after process PID dies (useful with -f)",
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
"tee": {
|
|
238
|
+
"man_url": "https://man7.org/linux/man-pages/man1/tee.1.html",
|
|
239
|
+
"use_cases": [
|
|
240
|
+
"Log output while still displaying it: make 2>&1 | tee build.log",
|
|
241
|
+
"Write to a file requiring sudo: echo 'line' | sudo tee /etc/config",
|
|
242
|
+
"Split output to multiple files: cmd | tee file1.txt file2.txt",
|
|
243
|
+
],
|
|
244
|
+
"gotchas": [
|
|
245
|
+
"tee overwrites by default -- use -a to append instead of truncating",
|
|
246
|
+
"tee buffers output which can delay display -- use stdbuf -oL for line buffering",
|
|
247
|
+
"Exit status reflects tee's success, not the upstream command's",
|
|
248
|
+
],
|
|
249
|
+
"related": ["cat", "script", "sponge", "redirect"],
|
|
250
|
+
"difficulty": "beginner",
|
|
251
|
+
"extra_flags": {
|
|
252
|
+
"-a": "Append to files instead of overwriting",
|
|
253
|
+
"-p": "Diagnose errors writing to non-pipes (GNU extension)",
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
"tr": {
|
|
257
|
+
"man_url": "https://man7.org/linux/man-pages/man1/tr.1.html",
|
|
258
|
+
"use_cases": [
|
|
259
|
+
"Convert case: echo 'hello' | tr 'a-z' 'A-Z'",
|
|
260
|
+
"Delete specific characters: tr -d '\\r' < file.txt (remove carriage returns)",
|
|
261
|
+
"Squeeze repeated characters: tr -s ' ' (collapse multiple spaces)",
|
|
262
|
+
],
|
|
263
|
+
"gotchas": [
|
|
264
|
+
"tr only works on stdin -- it cannot read files directly, so use redirection: tr 'a' 'b' < file",
|
|
265
|
+
"tr operates on individual characters, not strings -- tr 'abc' 'xyz' maps a->x, b->y, c->z",
|
|
266
|
+
"Character ranges depend on locale -- use LC_ALL=C for consistent behavior",
|
|
267
|
+
],
|
|
268
|
+
"related": ["sed", "cut", "awk", "iconv"],
|
|
269
|
+
"difficulty": "beginner",
|
|
270
|
+
"extra_flags": {
|
|
271
|
+
"-d": "Delete characters in SET1 from input",
|
|
272
|
+
"-s": "Squeeze repeated characters in the last specified set to a single character",
|
|
273
|
+
"-c": "Complement SET1 (operate on characters NOT in SET1)",
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
"uniq": {
|
|
277
|
+
"man_url": "https://man7.org/linux/man-pages/man1/uniq.1.html",
|
|
278
|
+
"use_cases": [
|
|
279
|
+
"Remove adjacent duplicate lines: sort file.txt | uniq",
|
|
280
|
+
"Count occurrences of each line: sort data.txt | uniq -c | sort -rn",
|
|
281
|
+
"Show only duplicate lines: sort names.txt | uniq -d",
|
|
282
|
+
],
|
|
283
|
+
"gotchas": [
|
|
284
|
+
"uniq only removes ADJACENT duplicates -- input must be sorted first or duplicates will be missed",
|
|
285
|
+
"uniq -c adds leading whitespace to counts, which can break downstream parsing",
|
|
286
|
+
"Field and character skipping (-f, -s) use 1-based indexing",
|
|
287
|
+
],
|
|
288
|
+
"related": ["sort", "awk", "comm", "wc"],
|
|
289
|
+
"difficulty": "beginner",
|
|
290
|
+
"extra_flags": {
|
|
291
|
+
"-c": "Prefix lines with the count of occurrences",
|
|
292
|
+
"-d": "Only print duplicate lines (one copy each)",
|
|
293
|
+
"-u": "Only print unique lines (lines that are not duplicated)",
|
|
294
|
+
"-i": "Ignore case when comparing lines",
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
"wc": {
|
|
298
|
+
"man_url": "https://man7.org/linux/man-pages/man1/wc.1.html",
|
|
299
|
+
"use_cases": [
|
|
300
|
+
"Count lines in a file: wc -l access.log",
|
|
301
|
+
"Count words in a document: wc -w essay.txt",
|
|
302
|
+
"Count files in a directory: ls | wc -l",
|
|
303
|
+
],
|
|
304
|
+
"gotchas": [
|
|
305
|
+
"wc -l counts newline characters, so a file without a trailing newline reports one fewer line than expected",
|
|
306
|
+
"wc includes the filename in output when given arguments -- use < redirection for just the number",
|
|
307
|
+
],
|
|
308
|
+
"related": ["cat", "awk", "grep", "sort"],
|
|
309
|
+
"difficulty": "beginner",
|
|
310
|
+
"extra_flags": {
|
|
311
|
+
"-c": "Print byte count",
|
|
312
|
+
"-m": "Print character count (differs from -c for multibyte encodings)",
|
|
313
|
+
"-L": "Print length of the longest line",
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
"xargs": {
|
|
317
|
+
"man_url": "https://man7.org/linux/man-pages/man1/xargs.1.html",
|
|
318
|
+
"use_cases": [
|
|
319
|
+
"Delete files found by find: find . -name '*.tmp' -print0 | xargs -0 rm",
|
|
320
|
+
"Run commands in parallel: find . -name '*.png' | xargs -P4 -I{} convert {} {}.webp",
|
|
321
|
+
"Pass grep results as arguments: grep -rl 'TODO' | xargs sed -i 's/TODO/DONE/g'",
|
|
322
|
+
],
|
|
323
|
+
"gotchas": [
|
|
324
|
+
"Filenames with spaces or special chars break xargs -- always use -0 with find -print0",
|
|
325
|
+
"xargs splits on whitespace and newlines by default, which is unsafe for arbitrary filenames",
|
|
326
|
+
"The -I flag disables parallelism and implies -L1 -- combine with -P for parallel + replacement",
|
|
327
|
+
],
|
|
328
|
+
"related": ["find", "parallel", "exec", "tee"],
|
|
329
|
+
"difficulty": "intermediate",
|
|
330
|
+
"extra_flags": {
|
|
331
|
+
"-0": "Use NUL as delimiter (pair with find -print0)",
|
|
332
|
+
"-P": "Run up to N processes in parallel",
|
|
333
|
+
"-I": "Replace occurrences of the placeholder with each input line",
|
|
334
|
+
"-n": "Use at most N arguments per command invocation",
|
|
335
|
+
"-t": "Print each command before executing (trace mode)",
|
|
336
|
+
},
|
|
337
|
+
},
|
|
338
|
+
|
|
339
|
+
# =========================================================================
|
|
340
|
+
# FILE SYSTEM NAVIGATION & MANAGEMENT
|
|
341
|
+
# =========================================================================
|
|
342
|
+
"cd": {
|
|
343
|
+
"man_url": "https://www.gnu.org/software/bash/manual/bash.html#index-cd",
|
|
344
|
+
"use_cases": [
|
|
345
|
+
"Navigate to a directory: cd /var/log",
|
|
346
|
+
"Return to previous directory: cd -",
|
|
347
|
+
"Go to home directory: cd or cd ~",
|
|
348
|
+
],
|
|
349
|
+
"gotchas": [
|
|
350
|
+
"cd in a subshell or pipe does not affect the parent shell's working directory",
|
|
351
|
+
"cd without error checking in scripts can cause commands to run in the wrong directory -- always use cd dir || exit 1",
|
|
352
|
+
"Symlinks can make pwd show a different path than expected -- use cd -P to resolve physical paths",
|
|
353
|
+
],
|
|
354
|
+
"related": ["pwd", "pushd", "popd", "dirs"],
|
|
355
|
+
"difficulty": "beginner",
|
|
356
|
+
},
|
|
357
|
+
"cmp": {
|
|
358
|
+
"man_url": "https://man7.org/linux/man-pages/man1/cmp.1.html",
|
|
359
|
+
"use_cases": [
|
|
360
|
+
"Check if two binary files are identical: cmp file1.bin file2.bin",
|
|
361
|
+
"Find the first differing byte: cmp -l original.bin modified.bin",
|
|
362
|
+
"Silent comparison for scripting: cmp -s a.bin b.bin && echo same",
|
|
363
|
+
],
|
|
364
|
+
"gotchas": [
|
|
365
|
+
"cmp compares bytes, not lines -- use diff for human-readable text comparison",
|
|
366
|
+
"cmp stops at the first difference by default -- use -l to list all differing bytes",
|
|
367
|
+
],
|
|
368
|
+
"related": ["diff", "md5sum", "sha256sum", "comm"],
|
|
369
|
+
"difficulty": "beginner",
|
|
370
|
+
"extra_flags": {
|
|
371
|
+
"-s": "Silent mode -- return exit status only, no output",
|
|
372
|
+
"-l": "Print byte number and differing byte values for all differences",
|
|
373
|
+
"-n": "Compare at most N bytes",
|
|
374
|
+
},
|
|
375
|
+
},
|
|
376
|
+
"cp": {
|
|
377
|
+
"man_url": "https://man7.org/linux/man-pages/man1/cp.1.html",
|
|
378
|
+
"use_cases": [
|
|
379
|
+
"Copy files preserving attributes: cp -a source/ dest/",
|
|
380
|
+
"Copy with a progress indicator: cp --verbose largefile.iso /backup/",
|
|
381
|
+
"Create backups before overwriting: cp --backup=numbered file.conf /etc/",
|
|
382
|
+
"Recursively copy directories: cp -r project/ project-backup/",
|
|
383
|
+
],
|
|
384
|
+
"gotchas": [
|
|
385
|
+
"cp without -r silently skips directories instead of erroring in some versions",
|
|
386
|
+
"cp overwrites destination files without warning unless -i is used",
|
|
387
|
+
"Symlinks are followed by default -- use -a or -d to preserve symlink structure",
|
|
388
|
+
],
|
|
389
|
+
"related": ["mv", "rsync", "scp", "install"],
|
|
390
|
+
"difficulty": "beginner",
|
|
391
|
+
"extra_flags": {
|
|
392
|
+
"-a": "Archive mode: preserve all attributes, recurse, and copy symlinks",
|
|
393
|
+
"-u": "Copy only when source is newer than destination or destination is missing",
|
|
394
|
+
"--reflink": "Use copy-on-write if supported by the filesystem (instant, space-efficient)",
|
|
395
|
+
},
|
|
396
|
+
},
|
|
397
|
+
"df": {
|
|
398
|
+
"man_url": "https://man7.org/linux/man-pages/man1/df.1.html",
|
|
399
|
+
"use_cases": [
|
|
400
|
+
"Check disk space usage: df -h",
|
|
401
|
+
"Show filesystem type: df -T",
|
|
402
|
+
"Check space on a specific mount: df -h /home",
|
|
403
|
+
],
|
|
404
|
+
"gotchas": [
|
|
405
|
+
"df shows filesystem-level usage, not directory-level -- use du for directory sizes",
|
|
406
|
+
"Reserved blocks (usually 5%) make df and du totals disagree on ext4 filesystems",
|
|
407
|
+
"Deleted-but-open files still consume space until the file handle is closed",
|
|
408
|
+
],
|
|
409
|
+
"related": ["du", "lsblk", "mount", "findmnt"],
|
|
410
|
+
"difficulty": "beginner",
|
|
411
|
+
"extra_flags": {
|
|
412
|
+
"-h": "Human-readable sizes (KB, MB, GB)",
|
|
413
|
+
"-i": "Show inode usage instead of block usage",
|
|
414
|
+
"-T": "Show filesystem type",
|
|
415
|
+
"--total": "Show a grand total at the bottom",
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
"diff": {
|
|
419
|
+
"man_url": "https://man7.org/linux/man-pages/man1/diff.1.html",
|
|
420
|
+
"use_cases": [
|
|
421
|
+
"Compare two files: diff file1.txt file2.txt",
|
|
422
|
+
"Generate a patch file: diff -u original.c modified.c > fix.patch",
|
|
423
|
+
"Compare directories recursively: diff -r dir1/ dir2/",
|
|
424
|
+
"Side-by-side comparison: diff -y file1.txt file2.txt",
|
|
425
|
+
],
|
|
426
|
+
"gotchas": [
|
|
427
|
+
"diff exit code 1 means differences found (not an error) -- check for exit code 2 for actual errors",
|
|
428
|
+
"Binary files are reported as differing without details -- use cmp -l for byte-level binary diffs",
|
|
429
|
+
"diff -r on large directory trees can be slow -- consider rsync --dry-run for large comparisons",
|
|
430
|
+
],
|
|
431
|
+
"related": ["cmp", "patch", "sdiff", "comm"],
|
|
432
|
+
"difficulty": "beginner",
|
|
433
|
+
"extra_flags": {
|
|
434
|
+
"-u": "Unified format (most common for patches)",
|
|
435
|
+
"-r": "Recursively compare subdirectories",
|
|
436
|
+
"-q": "Report only whether files differ, not the details",
|
|
437
|
+
"--color": "Colorize the output for terminal viewing",
|
|
438
|
+
},
|
|
439
|
+
},
|
|
440
|
+
"du": {
|
|
441
|
+
"man_url": "https://man7.org/linux/man-pages/man1/du.1.html",
|
|
442
|
+
"use_cases": [
|
|
443
|
+
"Find the largest directories: du -sh /* 2>/dev/null | sort -h",
|
|
444
|
+
"Check directory size: du -sh /var/log",
|
|
445
|
+
"Show disk usage per subdirectory: du -h --max-depth=1 /home",
|
|
446
|
+
],
|
|
447
|
+
"gotchas": [
|
|
448
|
+
"du measures disk allocation, not file size -- sparse files show smaller than ls -l reports",
|
|
449
|
+
"Hardlinked files may be counted multiple times unless --count-links is avoided",
|
|
450
|
+
"du without -s shows every subdirectory, producing overwhelming output on deep trees",
|
|
451
|
+
],
|
|
452
|
+
"related": ["df", "ncdu", "ls", "find"],
|
|
453
|
+
"difficulty": "beginner",
|
|
454
|
+
"extra_flags": {
|
|
455
|
+
"-s": "Show only the total for each argument (summary)",
|
|
456
|
+
"-h": "Human-readable sizes",
|
|
457
|
+
"--max-depth": "Limit directory depth for output",
|
|
458
|
+
"--exclude": "Exclude files matching a pattern",
|
|
459
|
+
},
|
|
460
|
+
},
|
|
461
|
+
"find": {
|
|
462
|
+
"man_url": "https://man7.org/linux/man-pages/man1/find.1.html",
|
|
463
|
+
"use_cases": [
|
|
464
|
+
"Find files by name: find /var -name '*.log' -mtime -7",
|
|
465
|
+
"Delete old temp files: find /tmp -type f -mtime +30 -delete",
|
|
466
|
+
"Find large files: find / -type f -size +100M 2>/dev/null",
|
|
467
|
+
"Execute commands on results: find . -name '*.py' -exec pylint {} +",
|
|
468
|
+
],
|
|
469
|
+
"gotchas": [
|
|
470
|
+
"find without -maxdepth searches the entire subtree, which can be very slow on large filesystems",
|
|
471
|
+
"-exec {} \\; runs one process per file (slow) -- use -exec {} + or pipe to xargs for batching",
|
|
472
|
+
"The order of predicates matters for performance -- put cheap tests like -name before expensive ones like -exec",
|
|
473
|
+
],
|
|
474
|
+
"related": ["locate", "fd", "xargs", "ls"],
|
|
475
|
+
"difficulty": "intermediate",
|
|
476
|
+
"extra_flags": {
|
|
477
|
+
"-maxdepth": "Limit search depth",
|
|
478
|
+
"-print0": "Output NUL-delimited results for safe piping to xargs -0",
|
|
479
|
+
"-newer": "Find files newer than a reference file",
|
|
480
|
+
"-empty": "Find empty files or directories",
|
|
481
|
+
"-regextype": "Specify regex dialect for -regex (posix-extended, etc.)",
|
|
482
|
+
},
|
|
483
|
+
},
|
|
484
|
+
"ls": {
|
|
485
|
+
"man_url": "https://man7.org/linux/man-pages/man1/ls.1.html",
|
|
486
|
+
"use_cases": [
|
|
487
|
+
"List files with details: ls -lah",
|
|
488
|
+
"Sort by modification time: ls -lt",
|
|
489
|
+
"Show hidden files: ls -A",
|
|
490
|
+
"Recursively list all files: ls -R",
|
|
491
|
+
],
|
|
492
|
+
"gotchas": [
|
|
493
|
+
"Parsing ls output is fragile and breaks on filenames with spaces or special characters -- use find or globbing instead",
|
|
494
|
+
"ls -l shows apparent file size, not disk usage -- use du for actual disk allocation",
|
|
495
|
+
"Color output can interfere with piping -- use ls --color=never or \\ls in scripts",
|
|
496
|
+
],
|
|
497
|
+
"related": ["tree", "find", "stat", "file"],
|
|
498
|
+
"difficulty": "beginner",
|
|
499
|
+
"extra_flags": {
|
|
500
|
+
"-S": "Sort by file size, largest first",
|
|
501
|
+
"-h": "Human-readable sizes with -l",
|
|
502
|
+
"-i": "Show inode numbers",
|
|
503
|
+
"--group-directories-first": "List directories before files",
|
|
504
|
+
},
|
|
505
|
+
},
|
|
506
|
+
"mkdir": {
|
|
507
|
+
"man_url": "https://man7.org/linux/man-pages/man1/mkdir.1.html",
|
|
508
|
+
"use_cases": [
|
|
509
|
+
"Create nested directories: mkdir -p /opt/app/config/ssl",
|
|
510
|
+
"Create with specific permissions: mkdir -m 700 /home/user/.ssh",
|
|
511
|
+
"Create multiple directories at once: mkdir -p src/{lib,bin,tests}",
|
|
512
|
+
],
|
|
513
|
+
"gotchas": [
|
|
514
|
+
"Without -p, mkdir fails if the parent directory does not exist",
|
|
515
|
+
"mkdir -p silently succeeds if the directory already exists, which can mask logic errors",
|
|
516
|
+
],
|
|
517
|
+
"related": ["rmdir", "install", "cp", "mv"],
|
|
518
|
+
"difficulty": "beginner",
|
|
519
|
+
"extra_flags": {
|
|
520
|
+
"-p": "Create parent directories as needed, no error if existing",
|
|
521
|
+
"-m": "Set permissions on creation (e.g., -m 755)",
|
|
522
|
+
"-v": "Print each directory as it is created",
|
|
523
|
+
},
|
|
524
|
+
},
|
|
525
|
+
"mv": {
|
|
526
|
+
"man_url": "https://man7.org/linux/man-pages/man1/mv.1.html",
|
|
527
|
+
"use_cases": [
|
|
528
|
+
"Rename files: mv old_name.txt new_name.txt",
|
|
529
|
+
"Move files to a directory: mv *.jpg /photos/",
|
|
530
|
+
"Safely move with backup: mv --backup=numbered config.yml /etc/",
|
|
531
|
+
],
|
|
532
|
+
"gotchas": [
|
|
533
|
+
"mv across filesystems performs a copy-then-delete, which is slow for large files and not atomic",
|
|
534
|
+
"mv overwrites the destination without warning unless -i (interactive) is used",
|
|
535
|
+
"Moving a directory into itself (mv dir dir/sub) causes an error, but the message can be confusing",
|
|
536
|
+
],
|
|
537
|
+
"related": ["cp", "rm", "rename", "rsync"],
|
|
538
|
+
"difficulty": "beginner",
|
|
539
|
+
"extra_flags": {
|
|
540
|
+
"-i": "Prompt before overwriting existing files",
|
|
541
|
+
"-n": "Do not overwrite existing files (no-clobber)",
|
|
542
|
+
"-v": "Explain what is being done",
|
|
543
|
+
"-t": "Specify the target directory (useful with xargs)",
|
|
544
|
+
},
|
|
545
|
+
},
|
|
546
|
+
"patch": {
|
|
547
|
+
"man_url": "https://man7.org/linux/man-pages/man1/patch.1.html",
|
|
548
|
+
"use_cases": [
|
|
549
|
+
"Apply a diff/patch file: patch -p1 < fix.patch",
|
|
550
|
+
"Reverse a previously applied patch: patch -R -p1 < fix.patch",
|
|
551
|
+
"Dry-run to test a patch: patch --dry-run -p1 < fix.patch",
|
|
552
|
+
],
|
|
553
|
+
"gotchas": [
|
|
554
|
+
"The -p (strip) level must match how the patch was created -- -p1 strips the first path component",
|
|
555
|
+
"Patches may fail on files that have been modified since the diff was generated (fuzz/offset warnings)",
|
|
556
|
+
"patch creates .orig backup files by default which can clutter the directory",
|
|
557
|
+
],
|
|
558
|
+
"related": ["diff", "git", "quilt", "merge"],
|
|
559
|
+
"difficulty": "intermediate",
|
|
560
|
+
"extra_flags": {
|
|
561
|
+
"-p": "Strip N leading path components from filenames in the patch",
|
|
562
|
+
"--dry-run": "Test the patch without applying it",
|
|
563
|
+
"-R": "Reverse the patch (unapply)",
|
|
564
|
+
"-b": "Create backup files (.orig) before patching",
|
|
565
|
+
},
|
|
566
|
+
},
|
|
567
|
+
"pwd": {
|
|
568
|
+
"man_url": "https://man7.org/linux/man-pages/man1/pwd.1.html",
|
|
569
|
+
"use_cases": [
|
|
570
|
+
"Print the current working directory: pwd",
|
|
571
|
+
"Get the physical path resolving symlinks: pwd -P",
|
|
572
|
+
"Store current directory in a variable: DIR=$(pwd)",
|
|
573
|
+
],
|
|
574
|
+
"gotchas": [
|
|
575
|
+
"pwd without -P shows the logical path including symlinks, which may not be the real filesystem path",
|
|
576
|
+
"The $PWD variable is faster than running the pwd command but may be stale after cd through symlinks",
|
|
577
|
+
],
|
|
578
|
+
"related": ["cd", "dirname", "basename", "realpath"],
|
|
579
|
+
"difficulty": "beginner",
|
|
580
|
+
},
|
|
581
|
+
"rm": {
|
|
582
|
+
"man_url": "https://man7.org/linux/man-pages/man1/rm.1.html",
|
|
583
|
+
"use_cases": [
|
|
584
|
+
"Remove files: rm unwanted_file.txt",
|
|
585
|
+
"Recursively remove a directory: rm -rf build/",
|
|
586
|
+
"Interactive removal for safety: rm -i important_files*",
|
|
587
|
+
],
|
|
588
|
+
"gotchas": [
|
|
589
|
+
"rm -rf / or rm -rf $VAR/ where VAR is unset can destroy the entire filesystem -- always quote variables and use set -u",
|
|
590
|
+
"There is no recycle bin -- deleted files are gone unless you have backups or use a trash utility",
|
|
591
|
+
"rm -f suppresses all errors including permission denied, making failures invisible",
|
|
592
|
+
],
|
|
593
|
+
"related": ["rmdir", "shred", "trash-put", "find"],
|
|
594
|
+
"difficulty": "beginner",
|
|
595
|
+
"extra_flags": {
|
|
596
|
+
"-i": "Prompt before each removal",
|
|
597
|
+
"-I": "Prompt once before removing more than 3 files or recursing",
|
|
598
|
+
"--preserve-root": "Refuse to operate on / (default on modern GNU rm)",
|
|
599
|
+
},
|
|
600
|
+
},
|
|
601
|
+
"tar": {
|
|
602
|
+
"man_url": "https://man7.org/linux/man-pages/man1/tar.1.html",
|
|
603
|
+
"use_cases": [
|
|
604
|
+
"Create a compressed archive: tar czf backup.tar.gz /home/user",
|
|
605
|
+
"Extract an archive: tar xzf archive.tar.gz",
|
|
606
|
+
"List archive contents: tar tzf archive.tar.gz",
|
|
607
|
+
"Extract specific files: tar xzf archive.tar.gz path/to/file",
|
|
608
|
+
],
|
|
609
|
+
"gotchas": [
|
|
610
|
+
"tar preserves absolute paths by default -- use -C or --strip-components to control extraction location",
|
|
611
|
+
"The order of flags matters in some versions -- put f last since it takes the filename argument",
|
|
612
|
+
"Extracting untrusted archives can overwrite files outside the target directory via path traversal (.. components)",
|
|
613
|
+
],
|
|
614
|
+
"related": ["gzip", "zip", "rsync", "cpio"],
|
|
615
|
+
"difficulty": "intermediate",
|
|
616
|
+
"extra_flags": {
|
|
617
|
+
"-C": "Change to directory before extracting",
|
|
618
|
+
"--strip-components": "Strip N leading path components on extraction",
|
|
619
|
+
"--exclude": "Exclude files matching a pattern",
|
|
620
|
+
"-j": "Use bzip2 compression instead of gzip",
|
|
621
|
+
"-J": "Use xz compression (best ratio, slower)",
|
|
622
|
+
"--totals": "Print total bytes written after creating archive",
|
|
623
|
+
},
|
|
624
|
+
},
|
|
625
|
+
"tree": {
|
|
626
|
+
"man_url": "https://man7.org/linux/man-pages/man1/tree.1.html",
|
|
627
|
+
"use_cases": [
|
|
628
|
+
"Visualize directory structure: tree -L 2",
|
|
629
|
+
"Show only directories: tree -d",
|
|
630
|
+
"Show files with sizes: tree -sh --du",
|
|
631
|
+
],
|
|
632
|
+
"gotchas": [
|
|
633
|
+
"tree is not installed by default on many systems -- install it via package manager",
|
|
634
|
+
"Running tree on large directories without -L produces enormous output",
|
|
635
|
+
"tree output is not easily parseable -- use find for scripting",
|
|
636
|
+
],
|
|
637
|
+
"related": ["ls", "find", "du", "exa"],
|
|
638
|
+
"difficulty": "beginner",
|
|
639
|
+
"extra_flags": {
|
|
640
|
+
"-L": "Limit display depth",
|
|
641
|
+
"-I": "Exclude files matching a pattern (e.g., -I 'node_modules|.git')",
|
|
642
|
+
"-a": "Show hidden files",
|
|
643
|
+
"--gitignore": "Respect .gitignore rules (recent versions)",
|
|
644
|
+
},
|
|
645
|
+
},
|
|
646
|
+
|
|
647
|
+
# =========================================================================
|
|
648
|
+
# VERSION CONTROL
|
|
649
|
+
# =========================================================================
|
|
650
|
+
"git": {
|
|
651
|
+
"man_url": "https://git-scm.com/docs",
|
|
652
|
+
"use_cases": [
|
|
653
|
+
"Track and manage source code changes across branches",
|
|
654
|
+
"Collaborate with teams via remotes: git push, pull, fetch",
|
|
655
|
+
"Review history and debug with git log, blame, bisect",
|
|
656
|
+
"Stage selective changes: git add -p for partial commits",
|
|
657
|
+
],
|
|
658
|
+
"gotchas": [
|
|
659
|
+
"git reset --hard destroys uncommitted changes with no recovery -- use git stash first",
|
|
660
|
+
"Force pushing (git push --force) rewrites shared history and can cause data loss for collaborators",
|
|
661
|
+
"Large binary files bloat the repository permanently -- use git-lfs for binaries",
|
|
662
|
+
],
|
|
663
|
+
"related": ["diff", "patch", "svn", "hg"],
|
|
664
|
+
"difficulty": "intermediate",
|
|
665
|
+
"extra_flags": {
|
|
666
|
+
"stash": "Temporarily shelve uncommitted changes",
|
|
667
|
+
"bisect": "Binary search through history to find a bug-introducing commit",
|
|
668
|
+
"reflog": "Show log of all ref updates (recovery tool for lost commits)",
|
|
669
|
+
"worktree": "Manage multiple working trees attached to the same repository",
|
|
670
|
+
},
|
|
671
|
+
},
|
|
672
|
+
|
|
673
|
+
# =========================================================================
|
|
674
|
+
# CONTAINERS
|
|
675
|
+
# =========================================================================
|
|
676
|
+
"docker": {
|
|
677
|
+
"man_url": "https://docs.docker.com/reference/cli/docker/",
|
|
678
|
+
"use_cases": [
|
|
679
|
+
"Run isolated applications: docker run -d --name web nginx",
|
|
680
|
+
"Build custom images: docker build -t myapp:latest .",
|
|
681
|
+
"Manage multi-container apps with compose: docker compose up -d",
|
|
682
|
+
"Inspect and debug containers: docker logs, exec, inspect",
|
|
683
|
+
],
|
|
684
|
+
"gotchas": [
|
|
685
|
+
"Containers run as root by default -- use --user or USER in Dockerfile for security",
|
|
686
|
+
"Data is lost when a container is removed unless volumes are used: docker run -v data:/app/data",
|
|
687
|
+
"docker system prune removes all unused images and containers -- add --volumes cautiously",
|
|
688
|
+
],
|
|
689
|
+
"related": ["podman", "kubectl", "containerd", "docker-compose"],
|
|
690
|
+
"difficulty": "intermediate",
|
|
691
|
+
"extra_flags": {
|
|
692
|
+
"system prune": "Remove all unused data (images, containers, networks)",
|
|
693
|
+
"exec -it": "Open interactive shell in a running container",
|
|
694
|
+
"stats": "Show live resource usage of running containers",
|
|
695
|
+
"inspect": "Return detailed JSON metadata about containers or images",
|
|
696
|
+
},
|
|
697
|
+
},
|
|
698
|
+
|
|
699
|
+
# =========================================================================
|
|
700
|
+
# SCHEDULING
|
|
701
|
+
# =========================================================================
|
|
702
|
+
"cron": {
|
|
703
|
+
"man_url": "https://man7.org/linux/man-pages/man8/cron.8.html",
|
|
704
|
+
"use_cases": [
|
|
705
|
+
"Schedule recurring system tasks like log rotation and backups",
|
|
706
|
+
"Run periodic maintenance scripts at off-peak hours",
|
|
707
|
+
"Automate report generation on a daily or weekly schedule",
|
|
708
|
+
],
|
|
709
|
+
"gotchas": [
|
|
710
|
+
"cron jobs run with a minimal environment -- PATH and other variables may differ from your interactive shell",
|
|
711
|
+
"cron has no dependency tracking -- if a job fails, subsequent dependent jobs still run",
|
|
712
|
+
"Cron emails output to the user by default -- redirect output or set MAILTO to avoid mail buildup",
|
|
713
|
+
],
|
|
714
|
+
"related": ["crontab", "at", "systemd-timer", "anacron"],
|
|
715
|
+
"difficulty": "intermediate",
|
|
716
|
+
},
|
|
717
|
+
"crontab": {
|
|
718
|
+
"man_url": "https://man7.org/linux/man-pages/man1/crontab.1.html",
|
|
719
|
+
"use_cases": [
|
|
720
|
+
"Edit your cron jobs: crontab -e",
|
|
721
|
+
"List current cron jobs: crontab -l",
|
|
722
|
+
"Install crontab from a file: crontab mycron.txt",
|
|
723
|
+
],
|
|
724
|
+
"gotchas": [
|
|
725
|
+
"crontab -r removes ALL your cron jobs without confirmation -- use crontab -l > backup first",
|
|
726
|
+
"Cron uses its own minimal PATH -- always use absolute paths for commands in crontab entries",
|
|
727
|
+
"The environment in cron is not your login shell -- source profiles or set variables explicitly",
|
|
728
|
+
],
|
|
729
|
+
"related": ["cron", "at", "systemctl", "anacron"],
|
|
730
|
+
"difficulty": "intermediate",
|
|
731
|
+
"extra_flags": {
|
|
732
|
+
"-e": "Edit the current user's crontab",
|
|
733
|
+
"-l": "List the current user's crontab entries",
|
|
734
|
+
"-r": "Remove the current user's crontab entirely",
|
|
735
|
+
"-u": "Operate on another user's crontab (requires root)",
|
|
736
|
+
},
|
|
737
|
+
},
|
|
738
|
+
"at": {
|
|
739
|
+
"man_url": "https://man7.org/linux/man-pages/man1/at.1.html",
|
|
740
|
+
"use_cases": [
|
|
741
|
+
"Schedule a one-time job: echo 'backup.sh' | at 2am tomorrow",
|
|
742
|
+
"Run a command after a delay: echo 'reboot' | at now + 5 minutes",
|
|
743
|
+
"List pending jobs: atq",
|
|
744
|
+
],
|
|
745
|
+
"gotchas": [
|
|
746
|
+
"at requires the atd daemon to be running -- check with systemctl status atd",
|
|
747
|
+
"at jobs inherit the current environment but not terminal -- output goes to mail by default",
|
|
748
|
+
"Time parsing is flexible but can be ambiguous -- always verify with atq after scheduling",
|
|
749
|
+
],
|
|
750
|
+
"related": ["crontab", "batch", "sleep", "nohup"],
|
|
751
|
+
"difficulty": "intermediate",
|
|
752
|
+
"extra_flags": {
|
|
753
|
+
"-m": "Send mail even if the job produces no output",
|
|
754
|
+
"-f": "Read commands from a file instead of stdin",
|
|
755
|
+
"-l": "List pending jobs (alias for atq)",
|
|
756
|
+
"-d": "Delete a job by number (alias for atrm)",
|
|
757
|
+
},
|
|
758
|
+
},
|
|
759
|
+
"batch": {
|
|
760
|
+
"man_url": "https://man7.org/linux/man-pages/man1/batch.1.html",
|
|
761
|
+
"use_cases": [
|
|
762
|
+
"Run a CPU-intensive job when system load drops: echo './compile.sh' | batch",
|
|
763
|
+
"Queue multiple jobs to run sequentially during idle time",
|
|
764
|
+
"Schedule resource-heavy tasks without impacting interactive users",
|
|
765
|
+
],
|
|
766
|
+
"gotchas": [
|
|
767
|
+
"batch waits until load average drops below 0.8 (or configured threshold) -- jobs may wait indefinitely on busy systems",
|
|
768
|
+
"Like at, batch requires the atd daemon to be running",
|
|
769
|
+
"Output is mailed to the user unless redirected -- check mail or redirect in the script",
|
|
770
|
+
],
|
|
771
|
+
"related": ["at", "nice", "nohup", "crontab"],
|
|
772
|
+
"difficulty": "intermediate",
|
|
773
|
+
},
|
|
774
|
+
|
|
775
|
+
# =========================================================================
|
|
776
|
+
# USER & GROUP ADMINISTRATION
|
|
777
|
+
# =========================================================================
|
|
778
|
+
"chgrp": {
|
|
779
|
+
"man_url": "https://man7.org/linux/man-pages/man1/chgrp.1.html",
|
|
780
|
+
"use_cases": [
|
|
781
|
+
"Change file group ownership: chgrp developers project/",
|
|
782
|
+
"Recursively change group: chgrp -R www-data /var/www",
|
|
783
|
+
"Change group using a reference file: chgrp --reference=ref.txt target.txt",
|
|
784
|
+
],
|
|
785
|
+
"gotchas": [
|
|
786
|
+
"Non-root users can only change to groups they belong to",
|
|
787
|
+
"chgrp follows symlinks by default -- use -h to change the symlink itself",
|
|
788
|
+
],
|
|
789
|
+
"related": ["chown", "chmod", "groups", "newgrp"],
|
|
790
|
+
"difficulty": "intermediate",
|
|
791
|
+
"extra_flags": {
|
|
792
|
+
"-R": "Operate recursively on directories",
|
|
793
|
+
"-h": "Change the symlink itself rather than the file it points to",
|
|
794
|
+
"--reference": "Use the group of a reference file",
|
|
795
|
+
},
|
|
796
|
+
},
|
|
797
|
+
"chmod": {
|
|
798
|
+
"man_url": "https://man7.org/linux/man-pages/man1/chmod.1.html",
|
|
799
|
+
"use_cases": [
|
|
800
|
+
"Make a script executable: chmod +x script.sh",
|
|
801
|
+
"Set specific permissions: chmod 644 config.yml",
|
|
802
|
+
"Recursively fix directory permissions: chmod -R u=rwX,go=rX /var/www",
|
|
803
|
+
],
|
|
804
|
+
"gotchas": [
|
|
805
|
+
"chmod 777 is almost never correct -- it allows anyone to read, write, and execute",
|
|
806
|
+
"The X (capital) permission sets execute only on directories and already-executable files -- useful for recursive operations",
|
|
807
|
+
"setuid/setgid bits (chmod u+s, g+s) are security-sensitive and often ignored on scripts",
|
|
808
|
+
],
|
|
809
|
+
"related": ["chown", "chgrp", "umask", "setfacl"],
|
|
810
|
+
"difficulty": "intermediate",
|
|
811
|
+
"extra_flags": {
|
|
812
|
+
"-R": "Change permissions recursively",
|
|
813
|
+
"--reference": "Copy permissions from a reference file",
|
|
814
|
+
"-v": "Output a diagnostic for every file processed",
|
|
815
|
+
},
|
|
816
|
+
},
|
|
817
|
+
"chown": {
|
|
818
|
+
"man_url": "https://man7.org/linux/man-pages/man1/chown.1.html",
|
|
819
|
+
"use_cases": [
|
|
820
|
+
"Change file owner: chown user:group file.txt",
|
|
821
|
+
"Recursively change ownership: chown -R www-data:www-data /var/www",
|
|
822
|
+
"Change only the user, preserving group: chown newuser file.txt",
|
|
823
|
+
],
|
|
824
|
+
"gotchas": [
|
|
825
|
+
"Only root can change file ownership -- non-root users get 'Operation not permitted'",
|
|
826
|
+
"chown follows symlinks by default -- use -h to change the symlink itself",
|
|
827
|
+
"Recursive chown on /etc or /var can break system services if done incorrectly",
|
|
828
|
+
],
|
|
829
|
+
"related": ["chmod", "chgrp", "stat", "id"],
|
|
830
|
+
"difficulty": "intermediate",
|
|
831
|
+
"extra_flags": {
|
|
832
|
+
"-R": "Operate recursively on directories",
|
|
833
|
+
"-h": "Change the symlink itself rather than the dereferenced file",
|
|
834
|
+
"--from": "Only change ownership if current owner:group matches",
|
|
835
|
+
"--reference": "Use the owner/group of a reference file",
|
|
836
|
+
},
|
|
837
|
+
},
|
|
838
|
+
"chpasswd": {
|
|
839
|
+
"man_url": "https://man7.org/linux/man-pages/man8/chpasswd.8.html",
|
|
840
|
+
"use_cases": [
|
|
841
|
+
"Batch-set passwords from a file: echo 'user:newpass' | chpasswd",
|
|
842
|
+
"Set encrypted passwords: chpasswd -e < hashed_passwords.txt",
|
|
843
|
+
"Automate user provisioning in scripts",
|
|
844
|
+
],
|
|
845
|
+
"gotchas": [
|
|
846
|
+
"Passwords on stdin are in cleartext unless -e is used -- secure the input source",
|
|
847
|
+
"Must be run as root",
|
|
848
|
+
"Input format is strictly username:password, one per line",
|
|
849
|
+
],
|
|
850
|
+
"related": ["passwd", "useradd", "usermod", "openssl"],
|
|
851
|
+
"difficulty": "advanced",
|
|
852
|
+
},
|
|
853
|
+
"chroot": {
|
|
854
|
+
"man_url": "https://man7.org/linux/man-pages/man1/chroot.1.html",
|
|
855
|
+
"use_cases": [
|
|
856
|
+
"Run a command in an isolated root filesystem: chroot /mnt/sysimage /bin/bash",
|
|
857
|
+
"Repair a broken system from a live CD by chrooting into the installed root",
|
|
858
|
+
"Create a minimal sandbox for building or testing software",
|
|
859
|
+
],
|
|
860
|
+
"gotchas": [
|
|
861
|
+
"chroot is NOT a security sandbox -- processes can escape with root access; use namespaces or containers instead",
|
|
862
|
+
"The chroot environment needs all required libraries and binaries (use ldd to check dependencies)",
|
|
863
|
+
"Must be run as root and requires a functional filesystem tree at the target path",
|
|
864
|
+
],
|
|
865
|
+
"related": ["unshare", "nsenter", "docker", "pivot_root"],
|
|
866
|
+
"difficulty": "advanced",
|
|
867
|
+
"extra_flags": {
|
|
868
|
+
"--userspec": "Run as specified USER:GROUP inside the chroot",
|
|
869
|
+
},
|
|
870
|
+
},
|
|
871
|
+
"doas": {
|
|
872
|
+
"man_url": "https://man.openbsd.org/doas",
|
|
873
|
+
"use_cases": [
|
|
874
|
+
"Run a command as root: doas apt update",
|
|
875
|
+
"Execute as another user: doas -u postgres psql",
|
|
876
|
+
"Simpler alternative to sudo with a minimal config file",
|
|
877
|
+
],
|
|
878
|
+
"gotchas": [
|
|
879
|
+
"doas is not installed by default on most Linux distros -- it must be installed and configured",
|
|
880
|
+
"Configuration file is /etc/doas.conf with different syntax than sudoers",
|
|
881
|
+
"Fewer features than sudo -- no command logging, session caching is limited",
|
|
882
|
+
],
|
|
883
|
+
"related": ["sudo", "su", "pkexec", "runuser"],
|
|
884
|
+
"difficulty": "intermediate",
|
|
885
|
+
},
|
|
886
|
+
"getfacl": {
|
|
887
|
+
"man_url": "https://man7.org/linux/man-pages/man1/getfacl.1.html",
|
|
888
|
+
"use_cases": [
|
|
889
|
+
"View access control lists on a file: getfacl /shared/project",
|
|
890
|
+
"Backup ACLs for restoration: getfacl -R /data > acl_backup.txt",
|
|
891
|
+
"Audit fine-grained permissions beyond standard Unix owner/group/other",
|
|
892
|
+
],
|
|
893
|
+
"gotchas": [
|
|
894
|
+
"ACLs require filesystem support (ext4, xfs) and must be mounted with acl option",
|
|
895
|
+
"cp and mv may not preserve ACLs by default -- use cp -a or rsync -A",
|
|
896
|
+
"ACL mask can restrict effective permissions below what individual entries grant",
|
|
897
|
+
],
|
|
898
|
+
"related": ["setfacl", "chmod", "chown", "ls"],
|
|
899
|
+
"difficulty": "advanced",
|
|
900
|
+
},
|
|
901
|
+
"groupadd": {
|
|
902
|
+
"man_url": "https://man7.org/linux/man-pages/man8/groupadd.8.html",
|
|
903
|
+
"use_cases": [
|
|
904
|
+
"Create a new group: groupadd developers",
|
|
905
|
+
"Create a system group: groupadd -r appservice",
|
|
906
|
+
"Create with specific GID: groupadd -g 1500 team",
|
|
907
|
+
],
|
|
908
|
+
"gotchas": [
|
|
909
|
+
"Requires root privileges",
|
|
910
|
+
"Does not automatically add any users to the new group -- use usermod -aG",
|
|
911
|
+
"System groups (-r) use GIDs from the system range defined in /etc/login.defs",
|
|
912
|
+
],
|
|
913
|
+
"related": ["groupdel", "groupmod", "useradd", "usermod"],
|
|
914
|
+
"difficulty": "intermediate",
|
|
915
|
+
},
|
|
916
|
+
"groupdel": {
|
|
917
|
+
"man_url": "https://man7.org/linux/man-pages/man8/groupdel.8.html",
|
|
918
|
+
"use_cases": [
|
|
919
|
+
"Remove a group: groupdel oldteam",
|
|
920
|
+
"Clean up groups after project decommissioning",
|
|
921
|
+
"Remove groups as part of user offboarding automation",
|
|
922
|
+
],
|
|
923
|
+
"gotchas": [
|
|
924
|
+
"Cannot delete a group that is any user's primary group -- change the user's primary group first",
|
|
925
|
+
"Files owned by the deleted group will show a numeric GID instead of a group name",
|
|
926
|
+
],
|
|
927
|
+
"related": ["groupadd", "groupmod", "userdel", "find"],
|
|
928
|
+
"difficulty": "intermediate",
|
|
929
|
+
},
|
|
930
|
+
"groupmod": {
|
|
931
|
+
"man_url": "https://man7.org/linux/man-pages/man8/groupmod.8.html",
|
|
932
|
+
"use_cases": [
|
|
933
|
+
"Rename a group: groupmod -n newname oldname",
|
|
934
|
+
"Change a group's GID: groupmod -g 2000 developers",
|
|
935
|
+
"Modify group properties during organizational changes",
|
|
936
|
+
],
|
|
937
|
+
"gotchas": [
|
|
938
|
+
"Changing GID does not update file ownership -- run find / -gid OLD_GID -exec chgrp NEW_GID {} +",
|
|
939
|
+
"Renaming a group does not update references in sudoers or other config files",
|
|
940
|
+
],
|
|
941
|
+
"related": ["groupadd", "groupdel", "usermod", "chgrp"],
|
|
942
|
+
"difficulty": "intermediate",
|
|
943
|
+
},
|
|
944
|
+
"groups": {
|
|
945
|
+
"man_url": "https://man7.org/linux/man-pages/man1/groups.1.html",
|
|
946
|
+
"use_cases": [
|
|
947
|
+
"List groups for the current user: groups",
|
|
948
|
+
"Check another user's groups: groups username",
|
|
949
|
+
"Verify group membership after usermod changes",
|
|
950
|
+
],
|
|
951
|
+
"gotchas": [
|
|
952
|
+
"Group changes from usermod -aG do not take effect until the user logs out and back in (or uses newgrp)",
|
|
953
|
+
"The first group listed is the primary group; others are supplementary",
|
|
954
|
+
],
|
|
955
|
+
"related": ["id", "newgrp", "usermod", "getent"],
|
|
956
|
+
"difficulty": "beginner",
|
|
957
|
+
},
|
|
958
|
+
"grpck": {
|
|
959
|
+
"man_url": "https://man7.org/linux/man-pages/man8/grpck.8.html",
|
|
960
|
+
"use_cases": [
|
|
961
|
+
"Verify integrity of /etc/group and /etc/gshadow: grpck",
|
|
962
|
+
"Fix inconsistencies in group databases: grpck -r (read-only check)",
|
|
963
|
+
"Run as part of system health audits",
|
|
964
|
+
],
|
|
965
|
+
"gotchas": [
|
|
966
|
+
"Must be run as root to modify files; use -r for read-only checks as non-root",
|
|
967
|
+
"Interactive prompts for fixes can be disruptive in automated scripts -- review changes carefully",
|
|
968
|
+
],
|
|
969
|
+
"related": ["pwck", "groupmod", "vigr", "getent"],
|
|
970
|
+
"difficulty": "advanced",
|
|
971
|
+
},
|
|
972
|
+
"id": {
|
|
973
|
+
"man_url": "https://man7.org/linux/man-pages/man1/id.1.html",
|
|
974
|
+
"use_cases": [
|
|
975
|
+
"Display current user's UID, GID, and groups: id",
|
|
976
|
+
"Check another user's identity: id username",
|
|
977
|
+
"Get just the numeric UID for scripting: id -u",
|
|
978
|
+
],
|
|
979
|
+
"gotchas": [
|
|
980
|
+
"id shows effective IDs, which may differ from real IDs when setuid programs are involved",
|
|
981
|
+
"Supplementary groups listed by id may not match groups if group membership was recently changed",
|
|
982
|
+
],
|
|
983
|
+
"related": ["whoami", "groups", "who", "w"],
|
|
984
|
+
"difficulty": "beginner",
|
|
985
|
+
"extra_flags": {
|
|
986
|
+
"-u": "Print only the effective user ID",
|
|
987
|
+
"-g": "Print only the effective group ID",
|
|
988
|
+
"-G": "Print all group IDs (supplementary groups)",
|
|
989
|
+
"-n": "Print names instead of numbers (use with -u, -g, or -G)",
|
|
990
|
+
},
|
|
991
|
+
},
|
|
992
|
+
"last": {
|
|
993
|
+
"man_url": "https://man7.org/linux/man-pages/man1/last.1.html",
|
|
994
|
+
"use_cases": [
|
|
995
|
+
"View recent login history: last",
|
|
996
|
+
"Check logins for a specific user: last username",
|
|
997
|
+
"View reboot history: last reboot",
|
|
998
|
+
],
|
|
999
|
+
"gotchas": [
|
|
1000
|
+
"last reads /var/log/wtmp which may be rotated -- old entries are lost after rotation",
|
|
1001
|
+
"Still-logged-in sessions show 'still logged in' with no duration",
|
|
1002
|
+
"System clock changes can make timestamps inaccurate in the log",
|
|
1003
|
+
],
|
|
1004
|
+
"related": ["lastlog", "w", "who", "utmpdump"],
|
|
1005
|
+
"difficulty": "beginner",
|
|
1006
|
+
"extra_flags": {
|
|
1007
|
+
"-n": "Show only the last N entries",
|
|
1008
|
+
"-x": "Show system shutdown and runlevel changes",
|
|
1009
|
+
"-F": "Show full login and logout times",
|
|
1010
|
+
},
|
|
1011
|
+
},
|
|
1012
|
+
"lastlog": {
|
|
1013
|
+
"man_url": "https://man7.org/linux/man-pages/man8/lastlog.8.html",
|
|
1014
|
+
"use_cases": [
|
|
1015
|
+
"Show last login time for all users: lastlog",
|
|
1016
|
+
"Check when a specific user last logged in: lastlog -u username",
|
|
1017
|
+
"Find accounts that have never logged in: lastlog | grep 'Never'",
|
|
1018
|
+
],
|
|
1019
|
+
"gotchas": [
|
|
1020
|
+
"lastlog reads /var/log/lastlog which uses a sparse file indexed by UID -- very high UIDs can make this file large",
|
|
1021
|
+
"Service accounts show 'Never logged in' which is expected, not a problem",
|
|
1022
|
+
],
|
|
1023
|
+
"related": ["last", "who", "w", "faillog"],
|
|
1024
|
+
"difficulty": "beginner",
|
|
1025
|
+
"extra_flags": {
|
|
1026
|
+
"-u": "Show information for a specific user",
|
|
1027
|
+
"-b": "Show users who have not logged in for N days",
|
|
1028
|
+
},
|
|
1029
|
+
},
|
|
1030
|
+
"newgrp": {
|
|
1031
|
+
"man_url": "https://man7.org/linux/man-pages/man1/newgrp.1.html",
|
|
1032
|
+
"use_cases": [
|
|
1033
|
+
"Switch active group without logging out: newgrp docker",
|
|
1034
|
+
"Activate a newly assigned supplementary group in the current session",
|
|
1035
|
+
"Create files with a specific group ownership by switching first",
|
|
1036
|
+
],
|
|
1037
|
+
"gotchas": [
|
|
1038
|
+
"newgrp starts a new shell -- exit to return to the original shell with original groups",
|
|
1039
|
+
"If a group password is set and the user is not a member, newgrp prompts for it",
|
|
1040
|
+
],
|
|
1041
|
+
"related": ["groups", "id", "usermod", "sg"],
|
|
1042
|
+
"difficulty": "intermediate",
|
|
1043
|
+
},
|
|
1044
|
+
"passwd": {
|
|
1045
|
+
"man_url": "https://man7.org/linux/man-pages/man1/passwd.1.html",
|
|
1046
|
+
"use_cases": [
|
|
1047
|
+
"Change your own password: passwd",
|
|
1048
|
+
"Change another user's password (root): passwd username",
|
|
1049
|
+
"Lock a user account: passwd -l username",
|
|
1050
|
+
"Set password expiration: passwd -x 90 username",
|
|
1051
|
+
],
|
|
1052
|
+
"gotchas": [
|
|
1053
|
+
"Password complexity rules are enforced by PAM -- they may reject passwords that seem fine to you",
|
|
1054
|
+
"passwd -l locks the account but does not disable SSH key authentication",
|
|
1055
|
+
"Expired passwords may lock users out of automated services that cannot handle interactive password changes",
|
|
1056
|
+
],
|
|
1057
|
+
"related": ["chpasswd", "usermod", "chage", "pwck"],
|
|
1058
|
+
"difficulty": "beginner",
|
|
1059
|
+
"extra_flags": {
|
|
1060
|
+
"-l": "Lock the account (prefix hash with !)",
|
|
1061
|
+
"-u": "Unlock a locked account",
|
|
1062
|
+
"-S": "Show password status (locked, set, etc.)",
|
|
1063
|
+
"-e": "Force password change at next login",
|
|
1064
|
+
},
|
|
1065
|
+
},
|
|
1066
|
+
"pwck": {
|
|
1067
|
+
"man_url": "https://man7.org/linux/man-pages/man8/pwck.8.html",
|
|
1068
|
+
"use_cases": [
|
|
1069
|
+
"Verify integrity of /etc/passwd and /etc/shadow: pwck",
|
|
1070
|
+
"Read-only check without modifications: pwck -r",
|
|
1071
|
+
"Part of routine system integrity audits",
|
|
1072
|
+
],
|
|
1073
|
+
"gotchas": [
|
|
1074
|
+
"Must be run as root to modify files; use -r for safe read-only checking",
|
|
1075
|
+
"May prompt to fix issues interactively -- not suitable for fully unattended automation without -r",
|
|
1076
|
+
],
|
|
1077
|
+
"related": ["grpck", "vipw", "passwd", "useradd"],
|
|
1078
|
+
"difficulty": "advanced",
|
|
1079
|
+
},
|
|
1080
|
+
"setfacl": {
|
|
1081
|
+
"man_url": "https://man7.org/linux/man-pages/man1/setfacl.1.html",
|
|
1082
|
+
"use_cases": [
|
|
1083
|
+
"Grant a specific user access: setfacl -m u:bob:rwx /shared/project",
|
|
1084
|
+
"Set default ACL for new files in a directory: setfacl -d -m g:dev:rw /shared",
|
|
1085
|
+
"Remove all ACLs: setfacl -b /shared/project",
|
|
1086
|
+
],
|
|
1087
|
+
"gotchas": [
|
|
1088
|
+
"The ACL mask limits effective permissions for named users and groups -- set mask explicitly if needed",
|
|
1089
|
+
"Default ACLs only apply to newly created files, not existing ones",
|
|
1090
|
+
"Some backup tools (tar without --acls) do not preserve ACLs",
|
|
1091
|
+
],
|
|
1092
|
+
"related": ["getfacl", "chmod", "chown", "umask"],
|
|
1093
|
+
"difficulty": "advanced",
|
|
1094
|
+
"extra_flags": {
|
|
1095
|
+
"-m": "Modify ACL entries",
|
|
1096
|
+
"-x": "Remove specific ACL entries",
|
|
1097
|
+
"-b": "Remove all ACL entries",
|
|
1098
|
+
"-R": "Apply recursively",
|
|
1099
|
+
"-d": "Set default ACL (for directories, applied to new files)",
|
|
1100
|
+
},
|
|
1101
|
+
},
|
|
1102
|
+
"su": {
|
|
1103
|
+
"man_url": "https://man7.org/linux/man-pages/man1/su.1.html",
|
|
1104
|
+
"use_cases": [
|
|
1105
|
+
"Switch to root: su -",
|
|
1106
|
+
"Run a command as another user: su -c 'whoami' postgres",
|
|
1107
|
+
"Open a login shell as another user: su - deploy",
|
|
1108
|
+
],
|
|
1109
|
+
"gotchas": [
|
|
1110
|
+
"su without - does not set a login environment -- PATH and other variables remain from the original user",
|
|
1111
|
+
"su requires the target user's password, while sudo requires your own password",
|
|
1112
|
+
"su to root leaves no audit trail of which user invoked it -- sudo provides better accountability",
|
|
1113
|
+
],
|
|
1114
|
+
"related": ["sudo", "doas", "runuser", "newgrp"],
|
|
1115
|
+
"difficulty": "intermediate",
|
|
1116
|
+
"extra_flags": {
|
|
1117
|
+
"-": "Start a login shell (full environment reset)",
|
|
1118
|
+
"-c": "Run a single command and return",
|
|
1119
|
+
"-s": "Use a specific shell instead of the target user's default",
|
|
1120
|
+
},
|
|
1121
|
+
},
|
|
1122
|
+
"sudo": {
|
|
1123
|
+
"man_url": "https://man7.org/linux/man-pages/man8/sudo.8.html",
|
|
1124
|
+
"use_cases": [
|
|
1125
|
+
"Run a command as root: sudo apt update",
|
|
1126
|
+
"Edit a protected file: sudo -e /etc/hosts (uses sudoedit)",
|
|
1127
|
+
"Run as another user: sudo -u postgres psql",
|
|
1128
|
+
"List allowed commands: sudo -l",
|
|
1129
|
+
],
|
|
1130
|
+
"gotchas": [
|
|
1131
|
+
"sudo caches credentials for a timeout period -- use sudo -k to invalidate the cache",
|
|
1132
|
+
"Environment variables are reset by default -- use sudo -E or specific env_keep in sudoers to preserve them",
|
|
1133
|
+
"Piping with sudo fails: 'sudo echo x > /etc/file' -- use 'echo x | sudo tee /etc/file' instead",
|
|
1134
|
+
],
|
|
1135
|
+
"related": ["su", "doas", "visudo", "pkexec"],
|
|
1136
|
+
"difficulty": "intermediate",
|
|
1137
|
+
"extra_flags": {
|
|
1138
|
+
"-u": "Run as a specified user instead of root",
|
|
1139
|
+
"-E": "Preserve the user's environment variables",
|
|
1140
|
+
"-l": "List commands the user is allowed to run",
|
|
1141
|
+
"-k": "Invalidate the cached credentials",
|
|
1142
|
+
"-i": "Start a login shell as root",
|
|
1143
|
+
},
|
|
1144
|
+
},
|
|
1145
|
+
"umask": {
|
|
1146
|
+
"man_url": "https://www.gnu.org/software/bash/manual/bash.html#index-umask",
|
|
1147
|
+
"use_cases": [
|
|
1148
|
+
"Check current umask: umask",
|
|
1149
|
+
"Set restrictive default permissions: umask 077 (owner-only access)",
|
|
1150
|
+
"Set permissive defaults for shared directories: umask 002",
|
|
1151
|
+
],
|
|
1152
|
+
"gotchas": [
|
|
1153
|
+
"umask is a mask (inverted) -- umask 022 means files get 644, not 022",
|
|
1154
|
+
"umask only affects new file creation, not existing files",
|
|
1155
|
+
"Different shells and login methods may set different default umasks -- check /etc/profile and PAM config",
|
|
1156
|
+
],
|
|
1157
|
+
"related": ["chmod", "mkdir", "touch", "install"],
|
|
1158
|
+
"difficulty": "intermediate",
|
|
1159
|
+
},
|
|
1160
|
+
"updatedb": {
|
|
1161
|
+
"man_url": "https://man7.org/linux/man-pages/man8/updatedb.8.html",
|
|
1162
|
+
"use_cases": [
|
|
1163
|
+
"Refresh the locate database: sudo updatedb",
|
|
1164
|
+
"Update with specific paths excluded: updatedb --prunepaths='/tmp /proc'",
|
|
1165
|
+
"Schedule via cron for daily index updates",
|
|
1166
|
+
],
|
|
1167
|
+
"gotchas": [
|
|
1168
|
+
"updatedb can be slow on large filesystems and generates significant I/O",
|
|
1169
|
+
"The database is stale between updates -- newly created files won't appear until next updatedb run",
|
|
1170
|
+
"By default only indexes paths accessible to the user running updatedb",
|
|
1171
|
+
],
|
|
1172
|
+
"related": ["locate", "mlocate", "plocate", "find"],
|
|
1173
|
+
"difficulty": "intermediate",
|
|
1174
|
+
},
|
|
1175
|
+
"mlocate": {
|
|
1176
|
+
"man_url": "https://man7.org/linux/man-pages/man1/locate.1.html",
|
|
1177
|
+
"use_cases": [
|
|
1178
|
+
"Find files by name quickly: locate nginx.conf",
|
|
1179
|
+
"Search with regex: locate -r '/etc/.*\\.conf$'",
|
|
1180
|
+
"Count matches: locate -c '*.py'",
|
|
1181
|
+
],
|
|
1182
|
+
"gotchas": [
|
|
1183
|
+
"Results may be stale -- run updatedb to refresh the database",
|
|
1184
|
+
"mlocate checks file permissions so users only see files they can access, unlike older locate",
|
|
1185
|
+
"Many distros have replaced mlocate with plocate for better performance",
|
|
1186
|
+
],
|
|
1187
|
+
"related": ["plocate", "updatedb", "find", "fd"],
|
|
1188
|
+
"difficulty": "beginner",
|
|
1189
|
+
},
|
|
1190
|
+
"plocate": {
|
|
1191
|
+
"man_url": "https://man7.org/linux/man-pages/man1/plocate.1.html",
|
|
1192
|
+
"use_cases": [
|
|
1193
|
+
"Fast file search by name: plocate config.yaml",
|
|
1194
|
+
"Case-insensitive search: plocate -i README",
|
|
1195
|
+
"Drop-in replacement for mlocate with significantly faster queries",
|
|
1196
|
+
],
|
|
1197
|
+
"gotchas": [
|
|
1198
|
+
"Still requires updatedb to be run (typically via cron) -- results lag behind filesystem changes",
|
|
1199
|
+
"Database format is incompatible with mlocate -- migration creates a new database",
|
|
1200
|
+
],
|
|
1201
|
+
"related": ["mlocate", "updatedb", "find", "fd"],
|
|
1202
|
+
"difficulty": "beginner",
|
|
1203
|
+
},
|
|
1204
|
+
"useradd": {
|
|
1205
|
+
"man_url": "https://man7.org/linux/man-pages/man8/useradd.8.html",
|
|
1206
|
+
"use_cases": [
|
|
1207
|
+
"Create a new user with home directory: useradd -m -s /bin/bash newuser",
|
|
1208
|
+
"Create a system user for a service: useradd -r -s /usr/sbin/nologin appuser",
|
|
1209
|
+
"Create user with specific UID and groups: useradd -u 1500 -G docker,dev newuser",
|
|
1210
|
+
],
|
|
1211
|
+
"gotchas": [
|
|
1212
|
+
"useradd does not create a home directory by default on many distros -- always use -m explicitly",
|
|
1213
|
+
"useradd does not set a password -- run passwd username afterward",
|
|
1214
|
+
"Debian/Ubuntu adduser is a friendlier wrapper -- useradd is the low-level tool",
|
|
1215
|
+
],
|
|
1216
|
+
"related": ["userdel", "usermod", "passwd", "adduser"],
|
|
1217
|
+
"difficulty": "intermediate",
|
|
1218
|
+
"extra_flags": {
|
|
1219
|
+
"-m": "Create the user's home directory",
|
|
1220
|
+
"-s": "Set the login shell",
|
|
1221
|
+
"-G": "Add to supplementary groups (comma-separated)",
|
|
1222
|
+
"-r": "Create a system account (no home, low UID)",
|
|
1223
|
+
"-e": "Set account expiration date (YYYY-MM-DD)",
|
|
1224
|
+
},
|
|
1225
|
+
},
|
|
1226
|
+
"userdel": {
|
|
1227
|
+
"man_url": "https://man7.org/linux/man-pages/man8/userdel.8.html",
|
|
1228
|
+
"use_cases": [
|
|
1229
|
+
"Remove a user account: userdel olduser",
|
|
1230
|
+
"Remove user and their home directory: userdel -r olduser",
|
|
1231
|
+
"Clean up deactivated accounts during offboarding",
|
|
1232
|
+
],
|
|
1233
|
+
"gotchas": [
|
|
1234
|
+
"userdel without -r leaves the home directory and mail spool behind -- orphaned files remain",
|
|
1235
|
+
"Cannot delete a user who is currently logged in -- use kill or loginctl to terminate sessions first",
|
|
1236
|
+
"Files owned by the deleted UID outside /home will show a numeric UID in ls -l",
|
|
1237
|
+
],
|
|
1238
|
+
"related": ["useradd", "usermod", "passwd", "find"],
|
|
1239
|
+
"difficulty": "intermediate",
|
|
1240
|
+
"extra_flags": {
|
|
1241
|
+
"-r": "Remove home directory and mail spool",
|
|
1242
|
+
"-f": "Force removal even if user is logged in (dangerous)",
|
|
1243
|
+
},
|
|
1244
|
+
},
|
|
1245
|
+
"usermod": {
|
|
1246
|
+
"man_url": "https://man7.org/linux/man-pages/man8/usermod.8.html",
|
|
1247
|
+
"use_cases": [
|
|
1248
|
+
"Add user to a group: usermod -aG docker username",
|
|
1249
|
+
"Change login shell: usermod -s /bin/zsh username",
|
|
1250
|
+
"Lock an account: usermod -L username",
|
|
1251
|
+
"Rename a user: usermod -l newname oldname",
|
|
1252
|
+
],
|
|
1253
|
+
"gotchas": [
|
|
1254
|
+
"usermod -G WITHOUT -a replaces all supplementary groups -- always use -aG to append",
|
|
1255
|
+
"Changes to groups do not take effect until the user logs out and back in",
|
|
1256
|
+
"Renaming a user (-l) does not rename the home directory -- use -d -m to move it",
|
|
1257
|
+
],
|
|
1258
|
+
"related": ["useradd", "userdel", "passwd", "groups"],
|
|
1259
|
+
"difficulty": "intermediate",
|
|
1260
|
+
"extra_flags": {
|
|
1261
|
+
"-aG": "Append to supplementary groups without removing existing ones",
|
|
1262
|
+
"-L": "Lock the user account (disable password login)",
|
|
1263
|
+
"-U": "Unlock a locked user account",
|
|
1264
|
+
"-l": "Change the login name",
|
|
1265
|
+
"-d": "Change the home directory (use -m to move files)",
|
|
1266
|
+
},
|
|
1267
|
+
},
|
|
1268
|
+
"users": {
|
|
1269
|
+
"man_url": "https://man7.org/linux/man-pages/man1/users.1.html",
|
|
1270
|
+
"use_cases": [
|
|
1271
|
+
"List currently logged-in usernames: users",
|
|
1272
|
+
"Quick check if anyone is logged in: users | wc -w",
|
|
1273
|
+
"Simple alternative to who when you only need usernames",
|
|
1274
|
+
],
|
|
1275
|
+
"gotchas": [
|
|
1276
|
+
"users reads /var/run/utmp which may not include all sessions (e.g., tmux/screen sub-sessions)",
|
|
1277
|
+
"Duplicate names appear if a user has multiple sessions",
|
|
1278
|
+
],
|
|
1279
|
+
"related": ["who", "w", "last", "id"],
|
|
1280
|
+
"difficulty": "beginner",
|
|
1281
|
+
},
|
|
1282
|
+
"vigr": {
|
|
1283
|
+
"man_url": "https://man7.org/linux/man-pages/man8/vigr.8.html",
|
|
1284
|
+
"use_cases": [
|
|
1285
|
+
"Safely edit /etc/group with locking: vigr",
|
|
1286
|
+
"Edit /etc/gshadow: vigr -s",
|
|
1287
|
+
"Prevent concurrent edits to group files by multiple admins",
|
|
1288
|
+
],
|
|
1289
|
+
"gotchas": [
|
|
1290
|
+
"Must be run as root",
|
|
1291
|
+
"Uses the $EDITOR or $VISUAL environment variable -- set these to your preferred editor",
|
|
1292
|
+
"Always run grpck after manual edits to verify file integrity",
|
|
1293
|
+
],
|
|
1294
|
+
"related": ["vipw", "grpck", "groupmod", "groupadd"],
|
|
1295
|
+
"difficulty": "advanced",
|
|
1296
|
+
},
|
|
1297
|
+
"vipw": {
|
|
1298
|
+
"man_url": "https://man7.org/linux/man-pages/man8/vipw.8.html",
|
|
1299
|
+
"use_cases": [
|
|
1300
|
+
"Safely edit /etc/passwd with locking: vipw",
|
|
1301
|
+
"Edit /etc/shadow: vipw -s",
|
|
1302
|
+
"Prevent corruption from concurrent edits to password files",
|
|
1303
|
+
],
|
|
1304
|
+
"gotchas": [
|
|
1305
|
+
"Must be run as root",
|
|
1306
|
+
"Always run pwck after manual edits to verify file integrity",
|
|
1307
|
+
"Syntax errors in /etc/passwd can lock all users out -- be very careful",
|
|
1308
|
+
],
|
|
1309
|
+
"related": ["vigr", "pwck", "passwd", "usermod"],
|
|
1310
|
+
"difficulty": "advanced",
|
|
1311
|
+
},
|
|
1312
|
+
"w": {
|
|
1313
|
+
"man_url": "https://man7.org/linux/man-pages/man1/w.1.html",
|
|
1314
|
+
"use_cases": [
|
|
1315
|
+
"See who is logged in and what they are doing: w",
|
|
1316
|
+
"Check system load and uptime at a glance",
|
|
1317
|
+
"Monitor idle times to find inactive sessions",
|
|
1318
|
+
],
|
|
1319
|
+
"gotchas": [
|
|
1320
|
+
"The WHAT column shows the current foreground process, which may not represent what the user is actually doing",
|
|
1321
|
+
"IDLE time resets on any terminal activity including background output",
|
|
1322
|
+
],
|
|
1323
|
+
"related": ["who", "uptime", "last", "users"],
|
|
1324
|
+
"difficulty": "beginner",
|
|
1325
|
+
},
|
|
1326
|
+
"wait": {
|
|
1327
|
+
"man_url": "https://www.gnu.org/software/bash/manual/bash.html#index-wait",
|
|
1328
|
+
"use_cases": [
|
|
1329
|
+
"Wait for all background jobs to finish: wait",
|
|
1330
|
+
"Wait for a specific PID: wait $pid",
|
|
1331
|
+
"Capture exit status of a background process: wait $pid; echo $?",
|
|
1332
|
+
],
|
|
1333
|
+
"gotchas": [
|
|
1334
|
+
"wait only works for children of the current shell -- it cannot wait for arbitrary PIDs from other processes",
|
|
1335
|
+
"In pipelines, $! gives the PID of the last command only -- store PIDs explicitly for multiple background jobs",
|
|
1336
|
+
"wait returns immediately with an error for PIDs that are not children of the current shell",
|
|
1337
|
+
],
|
|
1338
|
+
"related": ["jobs", "bg", "fg", "kill"],
|
|
1339
|
+
"difficulty": "intermediate",
|
|
1340
|
+
},
|
|
1341
|
+
"who": {
|
|
1342
|
+
"man_url": "https://man7.org/linux/man-pages/man1/who.1.html",
|
|
1343
|
+
"use_cases": [
|
|
1344
|
+
"List logged-in users with terminal info: who",
|
|
1345
|
+
"Show current user and terminal: who am i",
|
|
1346
|
+
"Check system boot time: who -b",
|
|
1347
|
+
],
|
|
1348
|
+
"gotchas": [
|
|
1349
|
+
"who reads utmp, which may not include pseudo-terminals from screen/tmux on all systems",
|
|
1350
|
+
"who am i may show nothing if not connected via a real login session (e.g., in a script)",
|
|
1351
|
+
],
|
|
1352
|
+
"related": ["w", "users", "last", "id"],
|
|
1353
|
+
"difficulty": "beginner",
|
|
1354
|
+
"extra_flags": {
|
|
1355
|
+
"-b": "Show time of last system boot",
|
|
1356
|
+
"-q": "Quick mode: show only usernames and count",
|
|
1357
|
+
"-H": "Print column headers",
|
|
1358
|
+
},
|
|
1359
|
+
},
|
|
1360
|
+
|
|
1361
|
+
# =========================================================================
|
|
1362
|
+
# SECURITY & TLS
|
|
1363
|
+
# =========================================================================
|
|
1364
|
+
"certbot": {
|
|
1365
|
+
"man_url": "https://eff-certbot.readthedocs.io/en/latest/",
|
|
1366
|
+
"use_cases": [
|
|
1367
|
+
"Obtain a free TLS certificate: certbot --nginx -d example.com",
|
|
1368
|
+
"Renew all certificates: certbot renew",
|
|
1369
|
+
"Test renewal without changing anything: certbot renew --dry-run",
|
|
1370
|
+
],
|
|
1371
|
+
"gotchas": [
|
|
1372
|
+
"Certificates expire every 90 days -- set up auto-renewal via cron or systemd timer",
|
|
1373
|
+
"Port 80 or 443 must be accessible for HTTP-01 or TLS-ALPN-01 validation",
|
|
1374
|
+
"Rate limits apply -- avoid repeated requests for the same domain during testing (use --staging)",
|
|
1375
|
+
],
|
|
1376
|
+
"related": ["openssl", "nginx", "apache2", "acme.sh"],
|
|
1377
|
+
"difficulty": "intermediate",
|
|
1378
|
+
"extra_flags": {
|
|
1379
|
+
"--nginx": "Use the Nginx plugin for automatic configuration",
|
|
1380
|
+
"--apache": "Use the Apache plugin for automatic configuration",
|
|
1381
|
+
"--standalone": "Run a temporary web server for validation",
|
|
1382
|
+
"--dry-run": "Test without saving certificates",
|
|
1383
|
+
"--staging": "Use the staging server to avoid rate limits during testing",
|
|
1384
|
+
},
|
|
1385
|
+
},
|
|
1386
|
+
|
|
1387
|
+
# =========================================================================
|
|
1388
|
+
# PYTHON RUNTIMES
|
|
1389
|
+
# =========================================================================
|
|
1390
|
+
"python2": {
|
|
1391
|
+
"man_url": "https://docs.python.org/2/using/cmdline.html",
|
|
1392
|
+
"use_cases": [
|
|
1393
|
+
"Run legacy Python 2 scripts: python2 legacy_script.py",
|
|
1394
|
+
"Check if Python 2 is still installed: python2 --version",
|
|
1395
|
+
"Maintain compatibility during Python 2 to 3 migration",
|
|
1396
|
+
],
|
|
1397
|
+
"gotchas": [
|
|
1398
|
+
"Python 2 reached end-of-life on January 1, 2020 -- no security patches are issued",
|
|
1399
|
+
"print is a statement in Python 2 (print 'x') but a function in Python 3 (print('x'))",
|
|
1400
|
+
"Integer division in Python 2 truncates: 3/2 == 1, not 1.5",
|
|
1401
|
+
],
|
|
1402
|
+
"related": ["python3", "pip", "virtualenv", "2to3"],
|
|
1403
|
+
"difficulty": "intermediate",
|
|
1404
|
+
},
|
|
1405
|
+
"python3": {
|
|
1406
|
+
"man_url": "https://docs.python.org/3/using/cmdline.html",
|
|
1407
|
+
"use_cases": [
|
|
1408
|
+
"Run Python scripts: python3 script.py",
|
|
1409
|
+
"Quick one-liner: python3 -c 'import json; print(json.dumps({\"a\":1}))'",
|
|
1410
|
+
"Start a simple HTTP server: python3 -m http.server 8080",
|
|
1411
|
+
"Create virtual environments: python3 -m venv .venv",
|
|
1412
|
+
],
|
|
1413
|
+
"gotchas": [
|
|
1414
|
+
"python may point to python2 or python3 depending on the system -- always use python3 explicitly",
|
|
1415
|
+
"pip install without --user or a venv may require sudo and can break system packages",
|
|
1416
|
+
"The GIL limits true parallelism for CPU-bound threads -- use multiprocessing for CPU-heavy work",
|
|
1417
|
+
],
|
|
1418
|
+
"related": ["pip", "venv", "ipython", "python2"],
|
|
1419
|
+
"difficulty": "beginner",
|
|
1420
|
+
"extra_flags": {
|
|
1421
|
+
"-m": "Run a module as a script (e.g., -m venv, -m http.server)",
|
|
1422
|
+
"-c": "Execute a command string",
|
|
1423
|
+
"-u": "Unbuffered stdout/stderr (useful in containers and pipes)",
|
|
1424
|
+
"-B": "Do not write .pyc bytecode cache files",
|
|
1425
|
+
},
|
|
1426
|
+
},
|
|
1427
|
+
|
|
1428
|
+
# =========================================================================
|
|
1429
|
+
# DEBUGGING & TRACING
|
|
1430
|
+
# =========================================================================
|
|
1431
|
+
"strace": {
|
|
1432
|
+
"man_url": "https://man7.org/linux/man-pages/man1/strace.1.html",
|
|
1433
|
+
"use_cases": [
|
|
1434
|
+
"Trace system calls of a process: strace -f ./program",
|
|
1435
|
+
"Debug why a program fails to open a file: strace -e trace=openat ./prog",
|
|
1436
|
+
"Measure syscall timing: strace -T -c ./program",
|
|
1437
|
+
"Attach to a running process: strace -p PID",
|
|
1438
|
+
],
|
|
1439
|
+
"gotchas": [
|
|
1440
|
+
"strace significantly slows down the traced process -- do not use on production without caution",
|
|
1441
|
+
"Output is very verbose by default -- use -e to filter specific syscall categories",
|
|
1442
|
+
"strace requires ptrace permissions -- seccomp or Yama may block it on hardened systems",
|
|
1443
|
+
],
|
|
1444
|
+
"related": ["ltrace", "perf", "gdb", "dmesg"],
|
|
1445
|
+
"difficulty": "advanced",
|
|
1446
|
+
"extra_flags": {
|
|
1447
|
+
"-f": "Follow child processes (fork/clone)",
|
|
1448
|
+
"-e": "Filter by syscall name or category (e.g., -e trace=network)",
|
|
1449
|
+
"-p": "Attach to a running process by PID",
|
|
1450
|
+
"-c": "Summarize syscall counts and times at exit",
|
|
1451
|
+
"-o": "Write trace output to a file instead of stderr",
|
|
1452
|
+
"-T": "Show time spent in each syscall",
|
|
1453
|
+
},
|
|
1454
|
+
},
|
|
1455
|
+
"ltrace": {
|
|
1456
|
+
"man_url": "https://man7.org/linux/man-pages/man1/ltrace.1.html",
|
|
1457
|
+
"use_cases": [
|
|
1458
|
+
"Trace library calls: ltrace ./program",
|
|
1459
|
+
"Debug shared library loading issues: ltrace -e dlopen ./prog",
|
|
1460
|
+
"Compare library call patterns between program versions",
|
|
1461
|
+
],
|
|
1462
|
+
"gotchas": [
|
|
1463
|
+
"ltrace does not work with statically linked binaries -- only dynamic libraries are traced",
|
|
1464
|
+
"Significant performance overhead, similar to strace",
|
|
1465
|
+
"May not be installed by default on minimal systems",
|
|
1466
|
+
],
|
|
1467
|
+
"related": ["strace", "ldd", "nm", "objdump"],
|
|
1468
|
+
"difficulty": "advanced",
|
|
1469
|
+
"extra_flags": {
|
|
1470
|
+
"-e": "Filter by library function name",
|
|
1471
|
+
"-p": "Attach to a running process by PID",
|
|
1472
|
+
"-c": "Summarize call counts and times",
|
|
1473
|
+
"-S": "Also trace system calls (like strace)",
|
|
1474
|
+
},
|
|
1475
|
+
},
|
|
1476
|
+
"perf": {
|
|
1477
|
+
"man_url": "https://man7.org/linux/man-pages/man1/perf.1.html",
|
|
1478
|
+
"use_cases": [
|
|
1479
|
+
"Profile CPU usage: perf record -g ./program && perf report",
|
|
1480
|
+
"Count hardware events: perf stat ./program",
|
|
1481
|
+
"Find performance hotspots: perf top",
|
|
1482
|
+
"Trace specific events: perf trace -e syscalls:sys_enter_write",
|
|
1483
|
+
],
|
|
1484
|
+
"gotchas": [
|
|
1485
|
+
"perf requires kernel support and may need kernel debug symbols for full stack traces",
|
|
1486
|
+
"Permission restrictions (perf_event_paranoid) may require root or sysctl tuning",
|
|
1487
|
+
"perf record files can grow very large for long-running programs -- use -F to lower sample frequency",
|
|
1488
|
+
],
|
|
1489
|
+
"related": ["strace", "valgrind", "gprof", "flamegraph"],
|
|
1490
|
+
"difficulty": "advanced",
|
|
1491
|
+
"extra_flags": {
|
|
1492
|
+
"record": "Collect performance data into perf.data",
|
|
1493
|
+
"report": "Display the recorded performance data",
|
|
1494
|
+
"stat": "Run a program and show event counters",
|
|
1495
|
+
"top": "Live system-wide profiling (like top for CPU events)",
|
|
1496
|
+
"-g": "Enable call graph (stack trace) recording",
|
|
1497
|
+
},
|
|
1498
|
+
},
|
|
1499
|
+
}
|