feat: Add Victor's outputs and screenshots for Linux problem set

This commit is contained in:
Payton Bates committed 2026-09-28 17:33:46 -04:00
1 parent 07f65b10e9
commit 2d30049c4e
15 files changed
+260 -14

No files matched your search

+229 -9
View File
@@ -10,13 +10,22 @@
\section{Purpose}
This chapter records a set of Linux shell commands run in the authors' working
user directories, and the solutions to the accompanying problem set.
Terminal input and output are copied directly from the shell, so each listing is
a record of an actual execution and serves as \gls{evidence} for this chapter.
The setup figures reproduce Payton's terminal session. Section~\ref{Section:LinuxEvidence}
adds Victor's actual problem-set execution screenshots as \gls{evidence}.
The command answers in Section~\ref{Section:LinuxSolutions} describe solutions;
where Victor used a different command, the difference is identified rather than
presented as an execution of the exact answer shown.
\section{Environment}
\index{Linux!environment}
The commands were run as the unprivileged user \texttt{payton} on Ubuntu~24.04
under the \gls{wsl} on the author's workstation, in a \texttt{bash} shell.
The setup figures were recorded by Payton as user \texttt{payton} on
Ubuntu~24.04 under \gls{wsl}, in a \texttt{bash} shell.
Victor independently performed the setup and problem set on September 27, 2026,
as user \texttt{victor} on host \texttt{Victori7}, running Ubuntu~24.04.3 LTS
under WSL~2. His working directory was \texttt{/home/victor/lx-test}.
The session was recorded with \texttt{script}; its original transcript is retained
with the evidence files. Normal commands ran without elevated privileges;
\texttt{sudo} was used for the append-only attribute in Q20.
\section{Working Directory Setup}
\index{Linux!working directory setup}
@@ -35,8 +44,8 @@ and Figure~{\ref{Figure:LxTestFiles}} shows the resulting file contents.
\item \texttt{printf "\ldots" > \textit{file}} writes the string to a file,
turning each \texttt{\textbackslash n} into a newline. The \texttt{>}
redirection creates the file or overwrites it if it already exists.
\texttt{printf} is used instead of \texttt{echo} because it handles escape
sequences the same way in every shell.
\texttt{printf} is used instead of \texttt{echo} for predictable formatting of these strings and newlines, avoiding
shell-dependent behavior of \texttt{echo}.
\item \texttt{dd} with \texttt{if=/dev/zero}, \texttt{of=blob.bin},
\texttt{bs=1K}, and \texttt{count=48} copies $48$ blocks of
$1\,\mathrm{KiB}$ from \texttt{/dev/zero}, producing a file of
@@ -66,7 +75,8 @@ Figure~\ref{Figure:LxTestDirectories} confirms each step of the setup:
points to \texttt{src/file1.txt}.
\item \textbf{Timestamp:} \texttt{old.txt} is empty and shows the modified timestamp (\texttt{Jan~2~2024}).
\item \textbf{Directories:} \texttt{archive}, \texttt{tmp}, and
\texttt{src/lib} report \texttt{total 0}, confirming they exist and are empty.
\texttt{src/lib} show no entries in the \texttt{ls -lA} listing, confirming they are empty
(\texttt{total 0} alone would not prove this).
\item \textbf{Permissions:} New files are \texttt{rw-r--r--} ($644$) and new
directories are \texttt{rwxr-xr-x} ($755$).
\end{itemize}
@@ -155,7 +165,9 @@ two three four five
\label{Figure:LxTestFiles}
\end{figure}
\clearpage
\section{Problem Set Solutions}
\label{Section:LinuxSolutions}
\index{Linux!problem set}
\subsection{Navigation and File Operations}
@@ -164,26 +176,44 @@ two three four five
\begin{lstlisting}[style=cmd]
$ pwd
\end{lstlisting}
\nopagebreak[4]
\texttt{pwd} prints only the current directory; Victor's result was \texttt{/home/victor/lx-test}.
\item List all entries in the current directory, one per line, including dotfiles.
\begin{lstlisting}[style=cmd]
$ ls -1a
\end{lstlisting}
\nopagebreak[4]
\texttt{-1} places each entry on a separate line; \texttt{-a} includes dotfiles and the \texttt{.} and \texttt{..} entries.
\item Copy \texttt{src/file1.txt} to \texttt{tmp/} only if \texttt{tmp} exists; do it verbosely.
\begin{lstlisting}[style=cmd]
$ [ -d tmp ] && cp -v src/file1.txt tmp/
\end{lstlisting}
\nopagebreak[4]
The directory test prevents copying when \texttt{tmp} is absent; \texttt{-v} reports the source and destination. Victor's output confirms the copy.
\item Move \texttt{old.txt} into \texttt{archive/} and keep its original timestamp.
\begin{lstlisting}[style=cmd]
$ mv old.txt archive/
\end{lstlisting}
\nopagebreak[4]
Moving within this working tree preserves the file's modification time. Victor verified that the timestamp remained January 2, 2024 at 03:04 local time.
\item Create a new empty file \texttt{notes.md} only if it doesn't already exist.
\begin{lstlisting}[style=cmd]
$ [ -e notes.md ] || touch notes.md
\end{lstlisting}
\nopagebreak[4]
The existence test avoids touching an existing file. Victor used the equivalent conditional \texttt{if} form and verified that the newly created file was zero bytes.
\item Show disk usage (human-readable) for the \texttt{src} directory only (not total FS).
\begin{lstlisting}[style=cmd]
$ du -sh src
\end{lstlisting}
\nopagebreak[4]
\texttt{-s} reports one directory total and \texttt{-h} uses readable units. Victor observed \texttt{16K}; allocated disk usage includes directory storage and is not simply text length.
\end{enumerate}
\subsection{Viewing and Searching}
@@ -192,26 +222,44 @@ $ du -sh src
\begin{lstlisting}[style=cmd]
$ cat -n sys.log
\end{lstlisting}
\nopagebreak[4]
\texttt{cat -n} numbers displayed lines. Victor used \texttt{nl -ba}, which also numbered all four lines in this file.
\item Show only the lines in \texttt{sys.log} that contain \texttt{ERROR} (case-sensitive).
\begin{lstlisting}[style=cmd]
$ grep 'ERROR' sys.log
\end{lstlisting}
\nopagebreak[4]
Without \texttt{-i}, matching is case-sensitive. The result was \texttt{ERROR fan fail}.
\item Count how many distinct words appear in \texttt{words.txt} (case-insensitive).
\begin{lstlisting}[style=cmd]
$ tr '[:upper:]' '[:lower:]' < words.txt | sort -u | wc -l
\end{lstlisting}
\nopagebreak[4]
Each input line contains one word. Lowercasing before sorting and deduplication treats \texttt{Gamma} and \texttt{gamma} as one value; the resulting count was three.
\item From \texttt{words.txt}, show lines that start with \texttt{g} or \texttt{G}.
\begin{lstlisting}[style=cmd]
$ grep '^[gG]' words.txt
\end{lstlisting}
\nopagebreak[4]
The anchor \texttt{\^{}} restricts the match to the start of the line, and \texttt{[gG]} accepts either case. The two matches were \texttt{Gamma} and \texttt{gamma}.
\item Display the first 2 lines of \texttt{people.csv} without using an editor.
\begin{lstlisting}[style=cmd]
$ head -n 2 people.csv
\end{lstlisting}
\nopagebreak[4]
The first two lines are the header and the first data row: \texttt{id,name,dept} and \texttt{1,Ada,EE}.
\item Show the last 3 lines of \texttt{sys.log} and keep following if the file grows.
\begin{lstlisting}[style=cmd]
$ tail -n 3 -f sys.log
\end{lstlisting}
\nopagebreak[4]
\texttt{-n 3} selects the last three lines; \texttt{-f} keeps following. Victor captured the initial three lines, then stopped following with Ctrl+C. No later appended-line test is claimed.
\end{enumerate}
\subsection{Text Processing}
@@ -220,18 +268,30 @@ $ tail -n 3 -f sys.log
\begin{lstlisting}[style=cmd]
$ tail -n +2 people.csv | cut -d, -f2
\end{lstlisting}
\nopagebreak[4]
\texttt{tail} skips the header and \texttt{cut} selects the comma-separated second field. Victor's equivalent \texttt{awk} command returned Ada, Linus, Grace, and Dennis.
\item Sort \texttt{words.txt} case-insensitively and remove duplicates.
\begin{lstlisting}[style=cmd]
$ sort -fu words.txt
\end{lstlisting}
\nopagebreak[4]
\texttt{-f} ignores case during comparison and \texttt{-u} removes duplicates. Victor's output was alpha, beta, and Gamma; the retained spelling need not be lowercased.
\item Replace every \texttt{three} with \texttt{3} in all files under \texttt{src/} in-place, creating \texttt{.bak} backups.
\begin{lstlisting}[style=cmd]
$ find src -type f -name '*.txt' -exec sed -i.bak 's/three/3/g' {} +
$ find src/ -type f ! -name '*.bak' -exec sed -i.bak 's/three/3/g' {} +
\end{lstlisting}
\nopagebreak[4]
\texttt{find} visits regular files recursively, excluding existing backups; \texttt{sed}'s \texttt{g} replaces every occurrence on each line. The original files are saved with the \texttt{.bak} suffix. Victor verified both edited content and original backups. Run once to avoid replacing the original backups on a later run.
\item Print the number of lines, words, and bytes for every \texttt{*.txt} file in \texttt{src/}.
\begin{lstlisting}[style=cmd]
$ wc src/*.txt
\end{lstlisting}
\nopagebreak[4]
By default, \texttt{wc} reports lines, words, and bytes. After Q15, Victor observed \texttt{1 4 15} and \texttt{1 4 16}, totaling \texttt{2 8 31}. Backup filenames do not match \texttt{*.txt}.
\end{enumerate}
\subsection{Permissions and Ownership}
@@ -240,18 +300,31 @@ $ wc src/*.txt
\begin{lstlisting}[style=cmd]
$ chmod 700 tmp/
\end{lstlisting}
\nopagebreak[4]
Mode \texttt{700} gives the owner read, write, and directory-search permissions, with none for group or others. Victor verified \texttt{drwx------}.
\item Give group execute permission to \texttt{src/lib} recursively without touching others/owner bits.
\begin{lstlisting}[style=cmd]
$ chmod -R g+x src/lib
\end{lstlisting}
\nopagebreak[4]
\texttt{g+x} adds only group execute; \texttt{-R} applies recursively. Victor's before/after value remained \texttt{755} because group execute was already enabled.
\item Show the numeric (octal) permissions of \texttt{src/file2.txt}.
\begin{lstlisting}[style=cmd]
$ stat -c '%a' src/file2.txt
\end{lstlisting}
\nopagebreak[4]
\texttt{\%a} requests octal permissions. Victor observed \texttt{644}: owner read/write, group and others read-only.
\item Make \texttt{notes.md} append-only for the owner via file attributes (if supported).
\begin{lstlisting}[style=cmd]
$ chmod 600 notes.md
$ sudo chattr +a notes.md
\end{lstlisting}
\nopagebreak[4]
Mode \texttt{600} limits ordinary access to the owner. The \texttt{a} attribute separately makes the file append-only; it is a file attribute, not an owner-specific permission bit. Victor's filesystem supported it: \texttt{lsattr} showed \texttt{a}, and \texttt{stat} showed \texttt{600}.
\end{enumerate}
\subsection{Links and Find}
@@ -260,14 +333,23 @@ $ sudo chattr +a notes.md
\begin{lstlisting}[style=cmd]
$ [ -L link-to-file1 ] && readlink link-to-file1
\end{lstlisting}
\nopagebreak[4]
\texttt{-L} checks the link itself and \texttt{readlink} prints its stored target. Victor confirmed \texttt{src/file1.txt}.
\item Find all regular files under the current tree larger than 40~KiB.
\begin{lstlisting}[style=cmd]
$ find . -type f -size +40k
\end{lstlisting}
\nopagebreak[4]
The size test selects regular files larger than 40 KiB. Victor used the equivalent byte threshold \texttt{+40960c}, printing \texttt{./blob.bin} with 49,152 bytes.
\item Find files modified in the last 10 minutes under \texttt{tmp/} and print their sizes.
\begin{lstlisting}[style=cmd]
$ find tmp/ -type f -mmin -10 -printf '%p %s\n'
\end{lstlisting}
\nopagebreak[4]
\texttt{-mmin -10} selects recent modifications; \texttt{\%s} prints bytes. Victor's command returned no matches because no regular file under \texttt{tmp/} met that time window.
\end{enumerate}
\subsection{Processes and Job Control}
@@ -276,18 +358,30 @@ $ find tmp/ -type f -mmin -10 -printf '%p %s\n'
\begin{lstlisting}[style=cmd]
$ pstree -p "$USER"
\end{lstlisting}
\nopagebreak[4]
\texttt{pstree -p} shows process relationships and PIDs for the specified user. Victor instead recorded his own processes with \texttt{ps -u} and \texttt{--forest}, exposing the shell, recording process, and child shell hierarchy.
\item Start \texttt{sleep 120} in the background and show its PID.
\begin{lstlisting}[style=cmd]
$ sleep 120 & echo $!
\end{lstlisting}
\nopagebreak[4]
The ampersand starts a background job; \texttt{\$!} is its PID. Victor captured and printed PID 2476 in his session; PIDs vary between executions.
\item Send a TERM signal to all \texttt{sleep} processes owned by you (don't use \texttt{kill -9}).
\begin{lstlisting}[style=cmd]
$ pkill -TERM -u "$USER" -x sleep
\end{lstlisting}
\nopagebreak[4]
\texttt{-u} restricts matches to the current user's processes and \texttt{-x} requires the exact name. Victor first checked for unrelated sleep processes, then terminated the test process. Waiting for it returned status 143 (128 + SIGTERM 15), and a final process search returned no matches.
\item Show the top 5 processes by memory usage (one-shot, not interactive).
\begin{lstlisting}[style=cmd]
$ ps aux --sort=-%mem | head -n 6
\end{lstlisting}
\nopagebreak[4]
Descending memory percentage puts the largest consumers first; six output lines include one header and five processes. Victor used descending resident-memory size (RSS in KiB) instead; his largest displayed process was \texttt{snapd} at 41,208 KiB.
\end{enumerate}
\subsection{Archiving and Compression}
@@ -296,14 +390,23 @@ $ ps aux --sort=-%mem | head -n 6
\begin{lstlisting}[style=cmd]
$ tar -czf src.tgz src/
\end{lstlisting}
\nopagebreak[4]
\texttt{-c} creates, \texttt{-z} compresses with gzip, and \texttt{-f} names the archive. The relative \texttt{src/} operand stores relative member paths.
\item List the contents of \texttt{src.tgz} without extracting.
\begin{lstlisting}[style=cmd]
$ tar -tzf src.tgz
\end{lstlisting}
\nopagebreak[4]
\texttt{-t} lists members without extraction. Victor's archive contained the two text files, their backups, and the \texttt{src/lib/} directory.
\item Extract only \texttt{file2.txt} from \texttt{src.tgz} into \texttt{tmp/}.
\begin{lstlisting}[style=cmd]
$ tar -xzvf src.tgz -C tmp/ --strip-components=1 src/file2.txt
\end{lstlisting}
\nopagebreak[4]
The named member limits extraction to one file, \texttt{-C} selects the destination, and stripping one path component produces \texttt{tmp/file2.txt}. Victor verified matching content with \texttt{cmp}.
\end{enumerate}
\subsection{Networking and System Information}
@@ -312,18 +415,30 @@ $ tar -xzvf src.tgz -C tmp/ --strip-components=1 src/file2.txt
\begin{lstlisting}[style=cmd]
$ ss -ltnp
\end{lstlisting}
\nopagebreak[4]
The flags select listening TCP sockets, numeric addresses, and process details when permitted. Victor saw two loopback listeners on port 53; the process column was blank in his unprivileged invocation, so no associated PIDs are claimed.
\item Print your default route (gateway) in a concise form.
\begin{lstlisting}[style=cmd]
$ ip route show default
\end{lstlisting}
\nopagebreak[4]
Victor's default route used gateway \texttt{172.23.224.1} through \texttt{eth0}. This is a local environment result, not a fixed gateway for every machine.
\item Display kernel name, release, and machine architecture.
\begin{lstlisting}[style=cmd]
$ uname -srm
\end{lstlisting}
\nopagebreak[4]
The requested fields identify the kernel and architecture. Victor observed Linux, release \texttt{6.18.33.2-microsoft-standard-WSL2}, and \texttt{x86\_64}.
\item Show the last 5 successful logins (or last sessions) on the system.
\begin{lstlisting}[style=cmd]
$ last -n 5
\end{lstlisting}
\nopagebreak[4]
Victor's last five available records were reboot/system-boot sessions rather than individual user logins. The record type is retained as shown; no successful user authentications are inferred.
\end{enumerate}
\subsection{Packages and Services (Debian/Ubuntu)}
@@ -332,28 +447,133 @@ $ last -n 5
\begin{lstlisting}[style=cmd]
$ dpkg-query -W -f='${Version}\n' coreutils
\end{lstlisting}
\nopagebreak[4]
The query reads installed-package metadata. Victor also printed the package name and observed \texttt{coreutils 9.4-3ubuntu6.3}.
\item Search available packages whose names contain \texttt{ripgrep}.
\begin{lstlisting}[style=cmd]
$ apt-cache search --names-only ripgrep
\end{lstlisting}
\nopagebreak[4]
The search checks package names in the local catalog. It found \texttt{ripgrep}; availability in the catalog does not establish that the package is installed.
\item Check whether service \texttt{cron} is active and print its status line only.
\begin{lstlisting}[style=cmd]
$ systemctl status cron | grep 'Active:'
\end{lstlisting}
\nopagebreak[4]
Filtering the detailed status selects its Active line. Victor used \texttt{systemctl is-active cron}, a concise active-state query, and recorded \texttt{active}.
\end{enumerate}
\subsection{Bash and Scripting}
\begin{enumerate}[label=\arabic*., leftmargin=*, resume]
\item Write a one-liner that loops over \texttt{*.txt} in \texttt{src/} and prints each file's name and line count.
\item Write a one-liner that loops over \texttt{*.txt} in \texttt{src/} and prints: : .
\textbf{Assumption:} the intended output is each file's name and line count, since
the supplied problem PDF displays an incomplete output format.
\begin{lstlisting}[style=cmd]
$ for f in src/*.txt; do echo "$f: $(wc -l < "$f")"; done
\end{lstlisting}
\nopagebreak[4]
For the stated line-count assumption, \texttt{wc -l} counts each file's lines and the loop prefixes its name. Victor's captured command instead printed filename and contents under a different interpretation of the incomplete prompt; Figure~\ref{Figure:HW2-J} documents that alternative.
\item Write a command that exports CSV rows where \texttt{dept == "CS"} to \texttt{cs.txt} (exclude header).
\begin{lstlisting}[style=cmd]
$ awk -F, 'NR>1 && $3=="CS"' people.csv > cs.txt
\end{lstlisting}
\nopagebreak[4]
The field separator is a comma; \texttt{NR>1} skips the header and field three selects CS. Victor verified that \texttt{cs.txt} contained \texttt{2,Linus,CS} and \texttt{4,Dennis,CS}.
\item Create a variable \texttt{X} with value 42, print it, then remove it from the environment.
\begin{lstlisting}[style=cmd]
$ export X=42; echo "$X"; unset X
\end{lstlisting}
\nopagebreak[4]
\texttt{export} adds the variable to the shell's exported environment; \texttt{unset} removes it. Victor printed \texttt{X=42}, then verified its absence using \texttt{printenv}.
\end{enumerate}
\section{Problem Set Execution Evidence}
\label{Section:LinuxEvidence}
These screenshots were supplied by Victor from his recorded September 27th
session. They show actual commands and outputs, grouped by problem-set section.
They supplement Payton's setup figures. Captions identify the question ranges;
the explanations above distinguish equivalent commands.
Empty output is retained where it is the observed result.
\begin{figure}[H]
\centering
\includegraphics[width=\linewidth,height=0.77\textwidth,keepaspectratio]{figures/03-section-a-q01-q06.png}
\caption{Q1--Q6: navigation and file operations. The moved file retains its original modification time, and the new notes file is empty.}
\label{Figure:HW2-A}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[width=\linewidth,height=0.77\textwidth,keepaspectratio]{figures/04-section-b-q07-q12.png}
\caption{Q7--Q12: viewing and searching. The final command remains in follow mode at capture; Victor subsequently stopped it with Ctrl+C.}
\label{Figure:HW2-B}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[width=\linewidth,height=0.77\textwidth,keepaspectratio]{figures/05-section-c-q13-q16.png}
\caption{Q13--Q16: text processing. Original text is retained in the backups, while the edited files total 31 bytes.}
\label{Figure:HW2-C}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[width=\linewidth,height=0.77\textwidth,keepaspectratio]{figures/06-section-d-q17-q20.png}
\caption{Q17--Q20: permissions and attributes. Group execute was already set; the append-only attribute and owner-only notes-file access are verified.}
\label{Figure:HW2-D}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[width=\linewidth,height=0.77\textwidth,keepaspectratio]{figures/07-section-e-q21-q23.png}
\caption{Q21--Q23: links and file searches. The recent-modification search returns no matches.}
\label{Figure:HW2-E}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[width=\linewidth,height=0.77\textwidth,keepaspectratio]{figures/08-section-f-q24-precheck.png}
\caption{Q24 and precheck: Victor records his process tree and confirms no existing user-owned sleep processes before the demonstration.}
\label{Figure:HW2-F1}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[width=\linewidth,height=0.77\textwidth,keepaspectratio]{figures/09-section-f-q25-q27.png}
\caption{Q25--Q27: background process, SIGTERM, exit status 143, and the five largest processes by resident-memory usage.}
\label{Figure:HW2-F2}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[width=\linewidth,height=0.77\textwidth,keepaspectratio]{figures/10-section-g-q28-q30.png}
\caption{Q28--Q30: archive creation, member listing, selected extraction, and successful content comparison.}
\label{Figure:HW2-G}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[width=\linewidth,height=0.77\textwidth,keepaspectratio]{figures/11-section-h-q31-q34.png}
\caption{Q31--Q34: sockets, route, kernel, and boot-session history. The unprivileged socket listing contains no process details.}
\label{Figure:HW2-H}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[width=\linewidth,height=0.77\textwidth,keepaspectratio]{figures/12-section-i-q35-q37.png}
\caption{Q35--Q37: installed coreutils version, available ripgrep package, and active cron state.}
\label{Figure:HW2-I}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[width=\linewidth,height=0.77\textwidth,keepaspectratio]{figures/13-section-j-q38-q40.png}
\caption{Q38--Q40: Victor uses the filename-and-contents interpretation for Q38, then verifies the CS export and removal of X from the environment.}
\label{Figure:HW2-J}
\end{figure}