feat: Add initial chapters and revisions for Assignment 2
This commit is contained in:
1 parent
05fff52f6f
commit
07f65b10e9
8 files changed
+410
-37
No files matched your search
@@ -50,8 +50,8 @@ rules govern access to the Linux hosts once they are provisioned.
|
||||
\item Interactive access is by \gls{ssh} public key only.
|
||||
Password authentication is disabled in the server configuration.
|
||||
The local account password recorded in \ChapterRef{Passwords} authenticates \texttt{sudo} only.
|
||||
\item Inbound \gls{ssh} is restricted by source address to the workstation
|
||||
listed above rather than opened to the public Internet.
|
||||
\item Inbound \gls{ssh} is restricted by source address to the local workstation
|
||||
rather than opened to the public Internet.
|
||||
\item Day-to-day work is performed by an unprivileged account. Administrative
|
||||
rights are granted per command rather than by general membership in the
|
||||
\texttt{sudo} group.
|
||||
|
||||
@@ -7,15 +7,10 @@
|
||||
|
||||
\section{Purpose}
|
||||
This document records the configuration of the development environment used for
|
||||
\CourseNumber\ and the working practices followed to produce it. For
|
||||
Assignment~\AssignmentNumber\ it covers three subjects: the \gls{kanban} that
|
||||
plans and tracks the assignment work, the hosts the environment will comprise,
|
||||
and the passwords that will govern accounts on those hosts.
|
||||
|
||||
It is a working record rather than a one-time report. Each subsequent
|
||||
assignment extends it, and the document history records what changed and when,
|
||||
so that the state of the environment at any point in the term can be recovered
|
||||
from this document alone.
|
||||
\CourseNumber\ and the working practices followed to produce it.
|
||||
Each chapter covers one subject, and each assignment adds or revises chapters
|
||||
as recorded in the document history. The latest revision for Assignment~\AssignmentNumber\
|
||||
adds \ChapterRef{LinuxCommands}.
|
||||
|
||||
\section{Scope and Constraints}
|
||||
\index{scope!constraints}
|
||||
@@ -23,15 +18,15 @@ The following constraints apply to this revision.
|
||||
|
||||
\begin{itemize}[leftmargin=*]
|
||||
\item The environment is \emph{specified} here, not provisioned. The hosts
|
||||
chapter names the machines to be configured and their intended roles; it
|
||||
does not claim that any host exists, and it therefore presents no
|
||||
execution \gls{evidence}.
|
||||
chapter names the machines to be configured and their intended roles.
|
||||
\item Command-line work was performed on a local workstation running
|
||||
Ubuntu~24.04 under the \gls{wsl}, not on a course
|
||||
host. Terminal listings are copied from actual executions.
|
||||
\item No credential, access token, private key, or account secret appears in
|
||||
this document. Password rules are documented by derivation hint only, as
|
||||
described in \ChapterRef{Passwords}.
|
||||
\item This is an individual submission. Teams have not yet been formalized, so
|
||||
the board described in \ChapterRef{KanbanSetup} is operated by a single
|
||||
person and every card is assigned to the author.
|
||||
\item This is a group submission. Chapters written before teams were formed
|
||||
were prepared individually and are identified as such in the document history.
|
||||
\item The document is authored in \LaTeX\ and compiled with pdf\LaTeX\ on
|
||||
Overleaf. The board is hosted in GitHub Projects. The planned environment
|
||||
targets Amazon EC2.
|
||||
@@ -42,18 +37,20 @@ The following constraints apply to this revision.
|
||||
\toprule \textbf{Item} & \textbf{Expectation}\\ \midrule
|
||||
Due date & \DueDate\\
|
||||
Instructor & \Instructor\\
|
||||
Team & \Author\\
|
||||
Board & GitHub Projects, \texttt{SSW 590} (private; captured as figures in \ChapterRef{KanbanSetup})\\
|
||||
Required deliverables & Compiled document; Kanban Setup, Passwords, and Hosts chapters; updated document history\\
|
||||
Team policy & Individual submission pending team formalization\\ \bottomrule
|
||||
Required deliverables & Compiled document; Linux Commands chapter; updated document history\\
|
||||
Team policy & Group submission\\ \bottomrule
|
||||
\end{tabularx}
|
||||
|
||||
\section{Document Organization}
|
||||
\index{document organization}
|
||||
\ChapterRef{KanbanSetup} describes the tool selected for the board, the steps
|
||||
taken to configure it, the policy governing each column, and the inventory of
|
||||
cards. \ChapterRef{Hosts} names the hosts to be configured and
|
||||
their roles. \ChapterRef{Passwords} defines the account and password rules for those hosts. \ChapterRef{AIPromptLog} records AI assistance used in preparing
|
||||
this document.
|
||||
taken to configure it, the policy governing each column, and the inventory of cards.
|
||||
\ChapterRef{Hosts} names the hosts to be configured and their roles.
|
||||
\ChapterRef{Passwords} defines the account and password rules for those hosts.
|
||||
\ChapterRef{LinuxCommands} records the Linux working-directory setup and the problem-set solutions.
|
||||
\ChapterRef{AIPromptLog} records AI assistance used in preparing this document.
|
||||
|
||||
This document is derived from the \CourseNumber\ assignment template credited on
|
||||
the Template Attribution page. The template's instructional example chapter and
|
||||
|
||||
@@ -0,0 +1,359 @@
|
||||
\chapter{Linux Commands}
|
||||
\ChapterLabel{LinuxCommands}
|
||||
\index{Linux}
|
||||
\index{Linux!command line}
|
||||
\index{Linux!working directory}
|
||||
\index{bash}
|
||||
\index{symbolic link}
|
||||
\index{file timestamps}
|
||||
|
||||
\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.
|
||||
|
||||
\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.
|
||||
|
||||
\section{Working Directory Setup}
|
||||
\index{Linux!working directory setup}
|
||||
Figure~\ref{Figure:LxTestSetup} shows the executed setup commands in the order given by
|
||||
the assignment. Figure~{\ref{Figure:LxTestDirectories}} shows the resulting directory structure,
|
||||
and Figure~{\ref{Figure:LxTestFiles}} shows the resulting file contents.
|
||||
|
||||
\subsection{What the Commands Do}
|
||||
\index{Linux!command explanations}
|
||||
\begin{sloppypar}
|
||||
\begin{itemize}[leftmargin=*]
|
||||
\item \texttt{mkdir -p \textasciitilde/lx-test \&\& cd \textasciitilde/lx-test}
|
||||
creates the directory, along with any missing parent directories, and does
|
||||
not fail if it already exists. The \texttt{\&\&} operator runs \texttt{cd}
|
||||
only if \texttt{mkdir} succeeds.
|
||||
\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.
|
||||
\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
|
||||
$48 \times 1024 = 49152$ zero bytes.
|
||||
\item \texttt{mkdir -p src/lib tmp archive} creates three directories in one
|
||||
command; \texttt{-p} creates \texttt{src} as the parent of \texttt{lib}.
|
||||
\item \texttt{ln -s src/file1.txt link-to-file1} creates a symbolic link. The
|
||||
stored target is the relative path \texttt{src/file1.txt}, which is
|
||||
resolved relative to the directory that contains the link.
|
||||
\item \texttt{touch -t 202401020304 old.txt} creates \texttt{old.txt} with a
|
||||
modification time of 2024-01-02 03:04 in local time.
|
||||
\item \texttt{ls -lA} lists each named directory in long format, including
|
||||
hidden entries but omitting \texttt{.} and \texttt{..}.
|
||||
\texttt{head} prints the first ten lines of each file under a
|
||||
file name header; every file here is shorter than ten
|
||||
lines, so each is shown in full.
|
||||
\end{itemize}
|
||||
\end{sloppypar}
|
||||
|
||||
\subsection{Interpretation of Results}
|
||||
\index{Linux!result interpretation}
|
||||
Figure~\ref{Figure:LxTestDirectories} confirms each step of the setup:
|
||||
\begin{itemize}[leftmargin=*]
|
||||
\item \textbf{File sizes:} \texttt{blob.bin} is $49152$ bytes, as computed
|
||||
above. Each text file's size equals its characters plus one byte per newline.
|
||||
\item \textbf{Symbolic link:} \texttt{link-to-file1} has type \texttt{l} and
|
||||
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.
|
||||
\item \textbf{Permissions:} New files are \texttt{rw-r--r--} ($644$) and new
|
||||
directories are \texttt{rwxr-xr-x} ($755$).
|
||||
\end{itemize}
|
||||
|
||||
\begin{figure}[htbp]
|
||||
\begin{lstlisting}[numbers=none, columns=fullflexible, keepspaces=true]
|
||||
payton@paytonpc:~$ mkdir -p ~/lx-test && cd ~/lx-test
|
||||
payton@paytonpc:~/lx-test$ printf "alpha\nbeta\nGamma\ngamma\nbeta\n" > words.txt
|
||||
payton@paytonpc:~/lx-test$ printf "id,name,dept\n1,Ada,EE\n2,Linus,CS\n3,Grace,EE\n4,Dennis,CS\n" > people.csv
|
||||
payton@paytonpc:~/lx-test$ printf "INFO boot ok\nWARN disk low\nERROR fan fail\nINFO shutdown\n" > sys.log
|
||||
payton@paytonpc:~/lx-test$ dd if=/dev/zero of=blob.bin bs=1K count=48 status=none
|
||||
payton@paytonpc:~/lx-test$ mkdir -p src/lib tmp archive
|
||||
payton@paytonpc:~/lx-test$ printf "one two three four\n" > src/file1.txt
|
||||
payton@paytonpc:~/lx-test$ printf "two three four five\n" > src/file2.txt
|
||||
payton@paytonpc:~/lx-test$ ln -s src/file1.txt link-to-file1
|
||||
payton@paytonpc:~/lx-test$ touch -t 202401020304 old.txt
|
||||
\end{lstlisting}
|
||||
\caption{Terminal input for setting up the \texttt{lx-test} working directory.}
|
||||
\label{Figure:LxTestSetup}
|
||||
\end{figure}
|
||||
|
||||
\begin{figure}[htbp]
|
||||
\begin{lstlisting}[numbers=none, columns=fullflexible, keepspaces=true]
|
||||
payton@paytonpc:~/lx-test$ ls -lA . src src/lib archive tmp
|
||||
.:
|
||||
total 72
|
||||
drwxr-xr-x 2 payton payton 4096 Sep 26 15:39 archive
|
||||
-rw-r--r-- 1 payton payton 49152 Sep 26 15:39 blob.bin
|
||||
lrwxrwxrwx 1 payton payton 13 Sep 26 15:39 link-to-file1 -> src/file1.txt
|
||||
-rw-r--r-- 1 payton payton 0 Jan 2 2024 old.txt
|
||||
-rw-r--r-- 1 payton payton 56 Sep 26 15:39 people.csv
|
||||
drwxr-xr-x 3 payton payton 4096 Sep 26 15:39 src
|
||||
-rw-r--r-- 1 payton payton 56 Sep 26 15:39 sys.log
|
||||
drwxr-xr-x 2 payton payton 4096 Sep 26 15:39 tmp
|
||||
-rw-r--r-- 1 payton payton 28 Sep 26 15:39 words.txt
|
||||
|
||||
archive:
|
||||
total 0
|
||||
|
||||
src:
|
||||
total 12
|
||||
-rw-r--r-- 1 payton payton 19 Sep 26 15:39 file1.txt
|
||||
-rw-r--r-- 1 payton payton 20 Sep 26 15:39 file2.txt
|
||||
drwxr-xr-x 2 payton payton 4096 Sep 26 15:39 lib
|
||||
|
||||
src/lib:
|
||||
total 0
|
||||
|
||||
tmp:
|
||||
total 0
|
||||
\end{lstlisting}
|
||||
\caption{Terminal output showing the resulting directories after \texttt{lx-test} setup.}
|
||||
\label{Figure:LxTestDirectories}
|
||||
\end{figure}
|
||||
|
||||
\begin{figure}[htbp]
|
||||
\begin{lstlisting}[numbers=none, columns=fullflexible, keepspaces=true]
|
||||
payton@paytonpc:~/lx-test$ head words.txt people.csv sys.log src/file1.txt src/file2.txt
|
||||
==> words.txt <==
|
||||
alpha
|
||||
beta
|
||||
Gamma
|
||||
gamma
|
||||
beta
|
||||
|
||||
==> people.csv <==
|
||||
id,name,dept
|
||||
1,Ada,EE
|
||||
2,Linus,CS
|
||||
3,Grace,EE
|
||||
4,Dennis,CS
|
||||
|
||||
==> sys.log <==
|
||||
INFO boot ok
|
||||
WARN disk low
|
||||
ERROR fan fail
|
||||
INFO shutdown
|
||||
|
||||
==> src/file1.txt <==
|
||||
one two three four
|
||||
|
||||
==> src/file2.txt <==
|
||||
two three four five
|
||||
\end{lstlisting}
|
||||
\caption{Terminal output showing the resulting file contents after \texttt{lx-test} setup.}
|
||||
\label{Figure:LxTestFiles}
|
||||
\end{figure}
|
||||
|
||||
\section{Problem Set Solutions}
|
||||
\index{Linux!problem set}
|
||||
|
||||
\subsection{Navigation and File Operations}
|
||||
\begin{enumerate}[label=\arabic*., leftmargin=*]
|
||||
\item Show your present working directory path only.
|
||||
\begin{lstlisting}[style=cmd]
|
||||
$ pwd
|
||||
\end{lstlisting}
|
||||
\item List all entries in the current directory, one per line, including dotfiles.
|
||||
\begin{lstlisting}[style=cmd]
|
||||
$ ls -1a
|
||||
\end{lstlisting}
|
||||
\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}
|
||||
\item Move \texttt{old.txt} into \texttt{archive/} and keep its original timestamp.
|
||||
\begin{lstlisting}[style=cmd]
|
||||
$ mv old.txt archive/
|
||||
\end{lstlisting}
|
||||
\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}
|
||||
\item Show disk usage (human-readable) for the \texttt{src} directory only (not total FS).
|
||||
\begin{lstlisting}[style=cmd]
|
||||
$ du -sh src
|
||||
\end{lstlisting}
|
||||
\end{enumerate}
|
||||
|
||||
\subsection{Viewing and Searching}
|
||||
\begin{enumerate}[label=\arabic*., leftmargin=*, resume]
|
||||
\item Print line numbers while displaying \texttt{sys.log}.
|
||||
\begin{lstlisting}[style=cmd]
|
||||
$ cat -n sys.log
|
||||
\end{lstlisting}
|
||||
\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}
|
||||
\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}
|
||||
\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}
|
||||
\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}
|
||||
\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}
|
||||
\end{enumerate}
|
||||
|
||||
\subsection{Text Processing}
|
||||
\begin{enumerate}[label=\arabic*., leftmargin=*, resume]
|
||||
\item From \texttt{people.csv}, print only the name column (2nd), excluding the header.
|
||||
\begin{lstlisting}[style=cmd]
|
||||
$ tail -n +2 people.csv | cut -d, -f2
|
||||
\end{lstlisting}
|
||||
\item Sort \texttt{words.txt} case-insensitively and remove duplicates.
|
||||
\begin{lstlisting}[style=cmd]
|
||||
$ sort -fu words.txt
|
||||
\end{lstlisting}
|
||||
\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' {} +
|
||||
\end{lstlisting}
|
||||
\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}
|
||||
\end{enumerate}
|
||||
|
||||
\subsection{Permissions and Ownership}
|
||||
\begin{enumerate}[label=\arabic*., leftmargin=*, resume]
|
||||
\item Make \texttt{tmp/} readable, writable, and searchable only by the owner.
|
||||
\begin{lstlisting}[style=cmd]
|
||||
$ chmod 700 tmp/
|
||||
\end{lstlisting}
|
||||
\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}
|
||||
\item Show the numeric (octal) permissions of \texttt{src/file2.txt}.
|
||||
\begin{lstlisting}[style=cmd]
|
||||
$ stat -c '%a' src/file2.txt
|
||||
\end{lstlisting}
|
||||
\item Make \texttt{notes.md} append-only for the owner via file attributes (if supported).
|
||||
\begin{lstlisting}[style=cmd]
|
||||
$ sudo chattr +a notes.md
|
||||
\end{lstlisting}
|
||||
\end{enumerate}
|
||||
|
||||
\subsection{Links and Find}
|
||||
\begin{enumerate}[label=\arabic*., leftmargin=*, resume]
|
||||
\item Verify whether \texttt{link-to-file1} is a symlink and show its target path.
|
||||
\begin{lstlisting}[style=cmd]
|
||||
$ [ -L link-to-file1 ] && readlink link-to-file1
|
||||
\end{lstlisting}
|
||||
\item Find all regular files under the current tree larger than 40~KiB.
|
||||
\begin{lstlisting}[style=cmd]
|
||||
$ find . -type f -size +40k
|
||||
\end{lstlisting}
|
||||
\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}
|
||||
\end{enumerate}
|
||||
|
||||
\subsection{Processes and Job Control}
|
||||
\begin{enumerate}[label=\arabic*., leftmargin=*, resume]
|
||||
\item Show your processes in a tree view.
|
||||
\begin{lstlisting}[style=cmd]
|
||||
$ pstree -p "$USER"
|
||||
\end{lstlisting}
|
||||
\item Start \texttt{sleep 120} in the background and show its PID.
|
||||
\begin{lstlisting}[style=cmd]
|
||||
$ sleep 120 & echo $!
|
||||
\end{lstlisting}
|
||||
\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}
|
||||
\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}
|
||||
\end{enumerate}
|
||||
|
||||
\subsection{Archiving and Compression}
|
||||
\begin{enumerate}[label=\arabic*., leftmargin=*, resume]
|
||||
\item Create a gzipped tar archive \texttt{src.tgz} from \texttt{src/} with relative paths.
|
||||
\begin{lstlisting}[style=cmd]
|
||||
$ tar -czf src.tgz src/
|
||||
\end{lstlisting}
|
||||
\item List the contents of \texttt{src.tgz} without extracting.
|
||||
\begin{lstlisting}[style=cmd]
|
||||
$ tar -tzf src.tgz
|
||||
\end{lstlisting}
|
||||
\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}
|
||||
\end{enumerate}
|
||||
|
||||
\subsection{Networking and System Information}
|
||||
\begin{enumerate}[label=\arabic*., leftmargin=*, resume]
|
||||
\item Show all listening TCP sockets with associated PIDs (no root assumptions).
|
||||
\begin{lstlisting}[style=cmd]
|
||||
$ ss -ltnp
|
||||
\end{lstlisting}
|
||||
\item Print your default route (gateway) in a concise form.
|
||||
\begin{lstlisting}[style=cmd]
|
||||
$ ip route show default
|
||||
\end{lstlisting}
|
||||
\item Display kernel name, release, and machine architecture.
|
||||
\begin{lstlisting}[style=cmd]
|
||||
$ uname -srm
|
||||
\end{lstlisting}
|
||||
\item Show the last 5 successful logins (or last sessions) on the system.
|
||||
\begin{lstlisting}[style=cmd]
|
||||
$ last -n 5
|
||||
\end{lstlisting}
|
||||
\end{enumerate}
|
||||
|
||||
\subsection{Packages and Services (Debian/Ubuntu)}
|
||||
\begin{enumerate}[label=\arabic*., leftmargin=*, resume]
|
||||
\item Show the installed version of package \texttt{coreutils}.
|
||||
\begin{lstlisting}[style=cmd]
|
||||
$ dpkg-query -W -f='${Version}\n' coreutils
|
||||
\end{lstlisting}
|
||||
\item Search available packages whose names contain \texttt{ripgrep}.
|
||||
\begin{lstlisting}[style=cmd]
|
||||
$ apt-cache search --names-only ripgrep
|
||||
\end{lstlisting}
|
||||
\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}
|
||||
\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.
|
||||
\begin{lstlisting}[style=cmd]
|
||||
$ for f in src/*.txt; do echo "$f: $(wc -l < "$f")"; done
|
||||
\end{lstlisting}
|
||||
\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}
|
||||
\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}
|
||||
\end{enumerate}
|
||||
Reference in new issue
Block a user