<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ro">
	<id>http://wiki.dcae.pub.ro/index.php?action=history&amp;feed=atom&amp;title=OS_Lab_5_-_Bash_Scripting</id>
	<title>OS Lab 5 - Bash Scripting - Revizia istoricului</title>
	<link rel="self" type="application/atom+xml" href="http://wiki.dcae.pub.ro/index.php?action=history&amp;feed=atom&amp;title=OS_Lab_5_-_Bash_Scripting"/>
	<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_5_-_Bash_Scripting&amp;action=history"/>
	<updated>2026-08-05T14:58:27Z</updated>
	<subtitle>Istoricul versiunilor pentru această pagină din wiki</subtitle>
	<generator>MediaWiki 1.35.14</generator>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_5_-_Bash_Scripting&amp;diff=8172&amp;oldid=prev</id>
		<title>Vserbu la 31 octombrie 2025 14:58</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_5_-_Bash_Scripting&amp;diff=8172&amp;oldid=prev"/>
		<updated>2025-10-31T14:58:04Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;a href=&quot;http://wiki.dcae.pub.ro/index.php?title=OS_Lab_5_-_Bash_Scripting&amp;amp;diff=8172&amp;amp;oldid=8171&quot;&gt;Afișare diferențe&lt;/a&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_5_-_Bash_Scripting&amp;diff=8171&amp;oldid=prev</id>
		<title>Vserbu la 31 octombrie 2025 14:54</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_5_-_Bash_Scripting&amp;diff=8171&amp;oldid=prev"/>
		<updated>2025-10-31T14:54:46Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;table class=&quot;diff diff-contentalign-left diff-editfont-monospace&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;ro&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Versiunea anterioară&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Versiunea de la data 31 octombrie 2025 14:54&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l43&quot; &gt;Linia 43:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Linia 43:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;/td&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;span id=&amp;quot;theoretical-background&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;span id=&amp;quot;theoretical-background&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&#039;diff-marker&#039;&gt;−&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== &lt;del class=&quot;diffchange diffchange-inline&quot;&gt;Theoretical Background &lt;/del&gt;==&lt;/div&gt;&lt;/td&gt;&lt;td class=&#039;diff-marker&#039;&gt;+&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== &lt;ins class=&quot;diffchange diffchange-inline&quot;&gt;Bash Scripting &lt;/ins&gt;==&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;/td&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;span id=&amp;quot;shebangs-and-script-execution&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;span id=&amp;quot;shebangs-and-script-execution&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_5_-_Bash_Scripting&amp;diff=8170&amp;oldid=prev</id>
		<title>Vserbu: Pagină nouă: &lt;span id=&quot;objectives&quot;&gt;&lt;/span&gt; == Objectives ==  Upon completion of this lab, you will be able to:  * Explain how the kernel executes scripts via the shebang mechanism and how bash...</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_5_-_Bash_Scripting&amp;diff=8170&amp;oldid=prev"/>
		<updated>2025-10-31T14:50:21Z</updated>

		<summary type="html">&lt;p&gt;Pagină nouă: &amp;lt;span id=&amp;quot;objectives&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; == Objectives ==  Upon completion of this lab, you will be able to:  * Explain how the kernel executes scripts via the shebang mechanism and how bash...&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Pagină nouă&lt;/b&gt;&lt;/p&gt;&lt;div&gt;&amp;lt;span id=&amp;quot;objectives&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Objectives ==&lt;br /&gt;
&lt;br /&gt;
Upon completion of this lab, you will be able to:&lt;br /&gt;
&lt;br /&gt;
* Explain how the kernel executes scripts via the shebang mechanism and how bash interprets script contents.&lt;br /&gt;
* Distinguish between shell builtins and external programs, understanding why certain commands must be builtins.&lt;br /&gt;
* Write scripts using proper variable quoting, recognizing that commands are fundamentally lists of strings.&lt;br /&gt;
* Use exit status to control script flow with conditionals and logical operators.&lt;br /&gt;
* Redirect file descriptors to control input and output streams.&lt;br /&gt;
* Implement loops with &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;for&amp;lt;/code&amp;gt;, incorporating command substitution.&lt;br /&gt;
* Define functions with proper variable scoping and work with arrays.&lt;br /&gt;
* Apply defensive scripting practices using &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;introduction&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
In previous labs, we explored processes, job control, and the permission model. These concepts form the foundation for understanding how programs execute and interact with the system. We now turn to bash scripting, which allows us to automate tasks and build tools without writing compiled programs.&lt;br /&gt;
&lt;br /&gt;
This lab emphasizes underlying mechanisms rather than syntax alone. We will examine how the kernel interprets shebangs, why certain commands must be shell builtins, and how bash&amp;#039;s command model treats everything as lists of strings. Understanding these foundations prepares you for the next lab, where we&amp;#039;ll explore signals and inter-process communication using bash scripts rather than C code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;system-requirements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== System Requirements ===&lt;br /&gt;
&lt;br /&gt;
A running instance of the course-provided Linux virtual machine with SSH or direct terminal access.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;required-packages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Required Packages ===&lt;br /&gt;
&lt;br /&gt;
All necessary utilities (&amp;lt;code&amp;gt;bash&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;seq&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;wc&amp;lt;/code&amp;gt;) are pre-installed. No additional packages are required.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;knowledge-prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Knowledge Prerequisites ===&lt;br /&gt;
&lt;br /&gt;
You should be familiar with:&lt;br /&gt;
&lt;br /&gt;
* Basic command-line navigation and file operations&lt;br /&gt;
* Process concepts from Lab 3 (PIDs, exit status)&lt;br /&gt;
* File permissions and the execute bit from Lab 4&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theoretical-background&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Theoretical Background ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;shebangs-and-script-execution&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Shebangs and Script Execution ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-kernels-role&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Kernel&amp;#039;s Role ====&lt;br /&gt;
&lt;br /&gt;
When you execute a file like &amp;lt;code&amp;gt;./script.sh&amp;lt;/code&amp;gt;, the kernel first checks the execute permission bit. If set, the kernel examines the file&amp;#039;s first bytes. If they are &amp;lt;code&amp;gt;#!&amp;lt;/code&amp;gt; (the &amp;amp;quot;shebang&amp;amp;quot;), the kernel reads the rest of that line as a path to an interpreter and re-executes the file using that interpreter.&lt;br /&gt;
&lt;br /&gt;
For example, a script beginning with &amp;lt;code&amp;gt;#!/bin/bash&amp;lt;/code&amp;gt; causes the kernel to execute &amp;lt;code&amp;gt;/bin/bash ./script.sh&amp;lt;/code&amp;gt;. This mechanism is general-purpose: the kernel doesn&amp;#039;t care whether the interpreter is bash, Python, or any other program.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Requirements for script execution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Execute bit must be set (&amp;lt;code&amp;gt;chmod +x script.sh&amp;lt;/code&amp;gt;)&lt;br /&gt;
* File must begin with a valid shebang pointing to an interpreter&lt;br /&gt;
* The interpreter itself must be an executable binary&lt;br /&gt;
&lt;br /&gt;
Without the execute bit, the kernel&amp;#039;s permission check fails. Without a shebang, the kernel cannot determine which interpreter to use, and behavior becomes system-dependent.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-shells-role&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Shell&amp;#039;s Role ====&lt;br /&gt;
&lt;br /&gt;
Once the kernel launches bash with your script as an argument, bash reads the script line by line, parsing each line according to its syntax rules. This involves expanding variables, performing word splitting, executing commands, and capturing results.&lt;br /&gt;
&lt;br /&gt;
The fundamental principle: &amp;#039;&amp;#039;&amp;#039;every command is a list of strings&amp;#039;&amp;#039;&amp;#039;. When bash executes &amp;lt;code&amp;gt;echo Hello World&amp;lt;/code&amp;gt;, it constructs a list of three strings: &amp;lt;code&amp;gt;&amp;amp;quot;echo&amp;amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;amp;quot;Hello&amp;amp;quot;&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;&amp;amp;quot;World&amp;amp;quot;&amp;lt;/code&amp;gt;. The first string identifies the command; remaining strings are arguments. This model applies universally to builtins, external programs, and functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;builtins-vs-external-programs&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Builtins vs External Programs ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;external-programs-and-path&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== External Programs and PATH ====&lt;br /&gt;
&lt;br /&gt;
When bash encounters a command that isn&amp;#039;t a builtin, it searches the directories in the &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt; environment variable for an executable with that name. When found, bash forks a child process and executes the program. Commands like &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;wc&amp;lt;/code&amp;gt; run in separate processes with their own address space.&lt;br /&gt;
&lt;br /&gt;
Use &amp;lt;code&amp;gt;which&amp;lt;/code&amp;gt; to locate external programs:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;which ls&lt;br /&gt;
# Output: /usr/bin/ls&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;shell-builtins&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Shell Builtins ====&lt;br /&gt;
&lt;br /&gt;
Builtins are commands built directly into bash. Use &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; to identify them:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;type cd&lt;br /&gt;
# Output: cd is a shell builtin&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Builtins exist for two reasons:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;1. Process State Modification&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Some commands must modify the shell&amp;#039;s own process state. Consider &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt;: if it were an external program, it would run in a child process, change that child&amp;#039;s directory, then exit. The parent shell&amp;#039;s directory would remain unchanged because child process state doesn&amp;#039;t propagate to parents. For &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt; to work, it must execute within the shell&amp;#039;s own process, making it a necessary builtin.&lt;br /&gt;
&lt;br /&gt;
Other state-modifying builtins include &amp;lt;code&amp;gt;ulimit&amp;lt;/code&amp;gt; (resource limits) and &amp;lt;code&amp;gt;umask&amp;lt;/code&amp;gt; (permission mask). In the next lab, we&amp;#039;ll see &amp;lt;code&amp;gt;export&amp;lt;/code&amp;gt; (environment variables) and &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt; (signal handlers).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;2. Performance&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Commands like &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;[[&amp;lt;/code&amp;gt; are builtins for performance, avoiding process creation overhead. They could theoretically be external programs, but making them builtins is faster.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-a-your-first-script-and-the-shebang&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise A: Your First Script and the Shebang ===&lt;br /&gt;
&lt;br /&gt;
This exercise demonstrates the kernel&amp;#039;s shebang mechanism and the importance of the execute bit.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol style=&amp;quot;list-style-type: decimal;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Create a script &amp;lt;code&amp;gt;/tmp/lab5_hello.sh&amp;lt;/code&amp;gt; containing:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;#!/bin/bash&lt;br /&gt;
echo &amp;quot;Hello from bash!&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Check the file&amp;#039;s permissions with &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt;. Note that the execute bit is not set.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Attempt to execute the script directly: &amp;lt;code&amp;gt;/tmp/lab5_hello.sh&amp;lt;/code&amp;gt;. This should fail with &amp;amp;quot;Permission denied&amp;amp;quot;.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Make the script executable with &amp;lt;code&amp;gt;chmod +x&amp;lt;/code&amp;gt; and verify with &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Execute the script directly. It should now succeed.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Execute the script by passing it to bash: &amp;lt;code&amp;gt;bash /tmp/lab5_hello.sh&amp;lt;/code&amp;gt;. Observe that this works even without the execute bit.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Create a second script without a shebang, make it executable, and attempt to run it. Observe the unpredictable behavior.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Clean up both test scripts.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable A:&amp;#039;&amp;#039;&amp;#039; Provide &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt; output before and after setting the execute bit, the &amp;amp;quot;Permission denied&amp;amp;quot; error, and the successful execution output.&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-b-builtins-vs-external-commands&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise B: Builtins vs External Commands ===&lt;br /&gt;
&lt;br /&gt;
This exercise demonstrates why certain commands must be builtins.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Use &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; to identify whether &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[[&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt; are builtins or external commands.&lt;br /&gt;
# Use &amp;lt;code&amp;gt;which&amp;lt;/code&amp;gt; to locate external programs. Try &amp;lt;code&amp;gt;which cd&amp;lt;/code&amp;gt; and note that it doesn&amp;#039;t find the builtin.&lt;br /&gt;
# Create &amp;lt;code&amp;gt;/tmp/lab5_cd_test.sh&amp;lt;/code&amp;gt; that prints the current directory, changes to &amp;lt;code&amp;gt;/tmp&amp;lt;/code&amp;gt;, and prints the directory again.&lt;br /&gt;
# Make the script executable and run it. Observe that the script successfully changes to &amp;lt;code&amp;gt;/tmp&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Check your shell&amp;#039;s current directory with &amp;lt;code&amp;gt;pwd&amp;lt;/code&amp;gt;. Note that your shell is still in the original directory.&lt;br /&gt;
# Test &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt; directly in your shell to confirm it works when run as a builtin.&lt;br /&gt;
# Clean up the test script.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable B:&amp;#039;&amp;#039;&amp;#039; Provide the &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; command outputs, the script output followed by your shell&amp;#039;s &amp;lt;code&amp;gt;pwd&amp;lt;/code&amp;gt; output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;variables-and-quoting&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Variables and Quoting ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-list-of-strings-model&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The List-of-Strings Model ====&lt;br /&gt;
&lt;br /&gt;
Variables in bash are expanded before commands execute. How they&amp;#039;re expanded determines the final command&amp;#039;s argument list.&lt;br /&gt;
&lt;br /&gt;
Consider:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;greeting=&amp;quot;Hello World&amp;quot;&lt;br /&gt;
echo $greeting&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bash expands &amp;lt;code&amp;gt;$greeting&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;&amp;amp;quot;Hello World&amp;amp;quot;&amp;lt;/code&amp;gt;, then performs &amp;#039;&amp;#039;&amp;#039;word splitting&amp;#039;&amp;#039;&amp;#039;, breaking it into separate strings at whitespace. The result: three strings passed to echo: &amp;lt;code&amp;gt;&amp;amp;quot;echo&amp;amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;amp;quot;Hello&amp;amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;amp;quot;World&amp;amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This works fine for echo, but consider:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;filename=&amp;quot;my document.txt&amp;quot;&lt;br /&gt;
ls $filename&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Word splitting breaks &amp;lt;code&amp;gt;&amp;amp;quot;my document.txt&amp;amp;quot;&amp;lt;/code&amp;gt; into &amp;lt;code&amp;gt;&amp;amp;quot;my&amp;amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;&amp;amp;quot;document.txt&amp;amp;quot;&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt; command receives two arguments and tries to list two separate files, which fails.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;quoting-fixes-this&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Quoting Fixes This ====&lt;br /&gt;
&lt;br /&gt;
Double quotes prevent word splitting:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls &amp;quot;$filename&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Now &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt; receives one argument: &amp;lt;code&amp;gt;&amp;amp;quot;my document.txt&amp;amp;quot;&amp;lt;/code&amp;gt;. The expansion happens, but the result stays as a single string.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Single quotes&amp;#039;&amp;#039;&amp;#039; prevent all expansion:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echo &amp;#039;$name&amp;#039;    # Outputs: $name (literal)&lt;br /&gt;
echo &amp;quot;$name&amp;quot;    # Outputs: the value of $name&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Best practice:&amp;#039;&amp;#039;&amp;#039; Always quote variables unless you specifically need word splitting. Write &amp;lt;code&amp;gt;&amp;amp;quot;$variable&amp;amp;quot;&amp;lt;/code&amp;gt; not &amp;lt;code&amp;gt;$variable&amp;lt;/code&amp;gt;. This makes scripts robust when variables contain spaces or special characters.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-c-quoting-and-word-splitting&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise C: Quoting and Word Splitting ===&lt;br /&gt;
&lt;br /&gt;
This exercise demonstrates why quoting variables is essential.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Create a file named &amp;lt;code&amp;gt;/tmp/my test file.txt&amp;lt;/code&amp;gt; (with spaces).&lt;br /&gt;
# Store the filename in a variable and try to list it without quotes: &amp;lt;code&amp;gt;ls $myfile&amp;lt;/code&amp;gt;. This will fail.&lt;br /&gt;
# Try again with proper quoting: &amp;lt;code&amp;gt;ls &amp;amp;quot;$myfile&amp;amp;quot;&amp;lt;/code&amp;gt;. This succeeds.&lt;br /&gt;
# Test echo with a variable containing multiple spaces, both with and without quotes.&lt;br /&gt;
# Create &amp;lt;code&amp;gt;/tmp/lab5_quote_test.sh&amp;lt;/code&amp;gt; that demonstrates variable quoting and array expansion both correctly and incorrectly. The script should show:&lt;br /&gt;
#* A variable with spaces, echoed with and without quotes&lt;br /&gt;
#* An array containing an element with spaces, iterated both ways&lt;br /&gt;
# Make the script executable and run it. Observe how the &amp;amp;quot;wrong way&amp;amp;quot; processes four items instead of three.&lt;br /&gt;
# Clean up the test files.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable C:&amp;#039;&amp;#039;&amp;#039; Provide the error from unquoted &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt;, the success from quoted &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt;, complete script output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exit-status-and-conditionals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exit Status and Conditionals ===&lt;br /&gt;
&lt;br /&gt;
Every command returns an exit status: a number from 0 to 255. By convention, 0 means success; non-zero means failure. Bash stores the last command&amp;#039;s exit status in &amp;lt;code&amp;gt;$?&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls /tmp&lt;br /&gt;
echo $?    # Outputs: 0 (success)&lt;br /&gt;
&lt;br /&gt;
ls /nonexistent&lt;br /&gt;
echo $?    # Outputs: non-zero (failure)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This exit status is bash&amp;#039;s fundamental true/false mechanism. An &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt; statement executes a command and checks if it succeeded:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;if grep -q &amp;quot;ERROR&amp;quot; /var/log/syslog; then&lt;br /&gt;
    echo &amp;quot;Errors found&amp;quot;&lt;br /&gt;
fi&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
If &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt; finds &amp;amp;quot;ERROR&amp;amp;quot;, it returns 0, and the &amp;lt;code&amp;gt;then&amp;lt;/code&amp;gt; block executes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the--command&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The &amp;lt;code&amp;gt;[[&amp;lt;/code&amp;gt; Command ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;[[&amp;lt;/code&amp;gt; builtin performs tests and returns an exit status. Despite unusual syntax, it&amp;#039;s conceptually just another command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;[[ -f &amp;quot;file.txt&amp;quot; ]]    # Returns 0 if file exists, 1 otherwise&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Common operators:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-f file&amp;lt;/code&amp;gt;: True if regular file exists&lt;br /&gt;
* &amp;lt;code&amp;gt;-d dir&amp;lt;/code&amp;gt;: True if directory exists&lt;br /&gt;
* &amp;lt;code&amp;gt;-e path&amp;lt;/code&amp;gt;: True if path exists&lt;br /&gt;
* &amp;lt;code&amp;gt;string1 == string2&amp;lt;/code&amp;gt;: True if strings equal&lt;br /&gt;
* &amp;lt;code&amp;gt;num1 -lt num2&amp;lt;/code&amp;gt;: True if num1 less than num2&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;if [[ -f &amp;quot;config.txt&amp;quot; ]]; then&lt;br /&gt;
    echo &amp;quot;Config file found&amp;quot;&lt;br /&gt;
fi&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;logical-operators&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Logical Operators ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;&amp;amp;amp;&amp;amp;amp;&amp;lt;/code&amp;gt; (AND):&amp;#039;&amp;#039;&amp;#039; Runs next command only if previous succeeded (returned 0):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;mkdir /tmp/newdir &amp;amp;&amp;amp; cd /tmp/newdir&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;||&amp;lt;/code&amp;gt; (OR):&amp;#039;&amp;#039;&amp;#039; Runs next command only if previous failed (returned non-zero):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cd /tmp/important || echo &amp;quot;Failed to change directory&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Explicit error ignoring:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -f /tmp/maybe-exists.txt || true&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The &amp;lt;code&amp;gt;|| true&amp;lt;/code&amp;gt; pattern documents that you&amp;#039;re intentionally ignoring potential errors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-d-exit-status-and-conditionals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise D: Exit Status and Conditionals ===&lt;br /&gt;
&lt;br /&gt;
This exercise explores exit status and conditional logic.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Execute &amp;lt;code&amp;gt;ls /tmp&amp;lt;/code&amp;gt; and check &amp;lt;code&amp;gt;$?&amp;lt;/code&amp;gt;. It should be 0 (success).&lt;br /&gt;
# Execute &amp;lt;code&amp;gt;ls /nonexistent&amp;lt;/code&amp;gt; (redirect stderr to &amp;lt;code&amp;gt;/dev/null&amp;lt;/code&amp;gt;) and check &amp;lt;code&amp;gt;$?&amp;lt;/code&amp;gt;. It should be non-zero.&lt;br /&gt;
# Test &amp;lt;code&amp;gt;[[ -f /etc/passwd ]]&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;[[ -f /nonexistent ]]&amp;lt;/code&amp;gt;, checking &amp;lt;code&amp;gt;$?&amp;lt;/code&amp;gt; after each.&lt;br /&gt;
# Create &amp;lt;code&amp;gt;/tmp/lab5_conditionals.sh&amp;lt;/code&amp;gt; that demonstrates:&lt;br /&gt;
#* File existence tests with &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;&lt;br /&gt;
#* The &amp;lt;code&amp;gt;&amp;amp;amp;&amp;amp;amp;&amp;lt;/code&amp;gt; operator for success chaining&lt;br /&gt;
#* The &amp;lt;code&amp;gt;||&amp;lt;/code&amp;gt; operator for failure handling&lt;br /&gt;
#* The &amp;lt;code&amp;gt;|| true&amp;lt;/code&amp;gt; pattern for explicit error ignoring&lt;br /&gt;
#* Using command exit status directly in &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt; (e.g., with &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;)&lt;br /&gt;
#* Numeric and string comparisons with &amp;lt;code&amp;gt;[[&amp;lt;/code&amp;gt;&lt;br /&gt;
# Make the script executable and run it. Observe the output from each test.&lt;br /&gt;
# Clean up the script.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable D:&amp;#039;&amp;#039;&amp;#039; Provide exit status values from steps 1-3, complete script output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;output-redirection-and-file-descriptors&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Output Redirection and File Descriptors ===&lt;br /&gt;
&lt;br /&gt;
Recall from Lab 3 that every process has file descriptors (FDs):&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FD 0:&amp;#039;&amp;#039;&amp;#039; Standard input (stdin)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FD 1:&amp;#039;&amp;#039;&amp;#039; Standard output (stdout)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FD 2:&amp;#039;&amp;#039;&amp;#039; Standard error (stderr)&lt;br /&gt;
&lt;br /&gt;
Redirection allows you to control where these streams go.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;basic-redirection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Basic Redirection ====&lt;br /&gt;
&lt;br /&gt;
Redirect stdout to a file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echo &amp;quot;output&amp;quot; &amp;gt; file.txt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Redirect stderr:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls /nonexistent 2&amp;gt; error.txt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Redirect both:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;command &amp;amp;&amp;gt; combined.txt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Or more explicitly:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;command &amp;gt; output.txt 2&amp;gt;&amp;amp;1&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Order matters! &amp;lt;code&amp;gt;2&amp;amp;gt;&amp;amp;amp;1&amp;lt;/code&amp;gt; must come after &amp;lt;code&amp;gt;&amp;amp;gt; output.txt&amp;lt;/code&amp;gt; to redirect stderr to where stdout is pointing.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;pipes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Pipes ====&lt;br /&gt;
&lt;br /&gt;
The pipe operator &amp;lt;code&amp;gt;|&amp;lt;/code&amp;gt; connects one command&amp;#039;s stdout to another&amp;#039;s stdin:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat /var/log/syslog | grep &amp;quot;ERROR&amp;quot; | wc -l&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Data flows left to right. The kernel creates an anonymous pipe, connecting the write end to the first command&amp;#039;s stdout and the read end to the second command&amp;#039;s stdin.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;input-redirection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Input Redirection ====&lt;br /&gt;
&lt;br /&gt;
Redirect stdin from a file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;wc -l &amp;lt; file.txt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This differs from &amp;lt;code&amp;gt;wc -l file.txt&amp;lt;/code&amp;gt;: with &amp;lt;code&amp;gt;&amp;amp;lt;&amp;lt;/code&amp;gt;, bash opens the file and connects it to stdin before running &amp;lt;code&amp;gt;wc&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;loops&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Loops ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-for-loop&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The &amp;lt;code&amp;gt;for&amp;lt;/code&amp;gt; Loop ====&lt;br /&gt;
&lt;br /&gt;
Iterates over a list of values:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;for item in value1 value2 value3; do&lt;br /&gt;
    echo &amp;quot;$item&amp;quot;&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Common sources for the list:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Filename expansion (globbing):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;for file in *.txt; do&lt;br /&gt;
    echo &amp;quot;Processing $file&amp;quot;&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Command substitution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;for i in $(seq 1 10); do&lt;br /&gt;
    echo &amp;quot;Number $i&amp;quot;&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-while-loop&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt; Loop ====&lt;br /&gt;
&lt;br /&gt;
Executes while a command succeeds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;counter=1&lt;br /&gt;
while [[ $counter -le 5 ]]; do&lt;br /&gt;
    echo &amp;quot;Iteration $counter&amp;quot;&lt;br /&gt;
    counter=$((counter + 1))&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Reading files line-by-line:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;while read -r line; do&lt;br /&gt;
    echo &amp;quot;Line: $line&amp;quot;&lt;br /&gt;
done &amp;lt; file.txt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The &amp;lt;code&amp;gt;read&amp;lt;/code&amp;gt; command returns 0 while reading lines successfully, and non-zero at end-of-file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-e-file-descriptor-redirection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise E: File Descriptor Redirection ===&lt;br /&gt;
&lt;br /&gt;
This exercise explores redirection, connecting to Lab 3&amp;#039;s file descriptor concepts.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Redirect stdout to a file with &amp;lt;code&amp;gt;&amp;amp;gt;&amp;lt;/code&amp;gt;, then display the file contents.&lt;br /&gt;
# Redirect stderr to a file with &amp;lt;code&amp;gt;2&amp;amp;gt;&amp;lt;/code&amp;gt; by trying to list a nonexistent directory.&lt;br /&gt;
# Redirect both stdout and stderr to the same file using &amp;lt;code&amp;gt;&amp;amp;amp;&amp;amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Demonstrate the &amp;lt;code&amp;gt;2&amp;amp;gt;&amp;amp;amp;1&amp;lt;/code&amp;gt; pattern with explicit redirection order.&lt;br /&gt;
# Use a pipe to chain &amp;lt;code&amp;gt;cat /etc/passwd&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;grep &amp;amp;quot;root&amp;amp;quot;&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;cut&amp;lt;/code&amp;gt; to extract the username.&lt;br /&gt;
# Create &amp;lt;code&amp;gt;/tmp/lab5_redirect.sh&amp;lt;/code&amp;gt; that demonstrates:&lt;br /&gt;
#* Output redirection with &amp;lt;code&amp;gt;&amp;amp;gt;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;&amp;amp;gt;&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
#* Stderr capture with &amp;lt;code&amp;gt;2&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
#* Combined stream redirection with &amp;lt;code&amp;gt;&amp;amp;amp;&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
#* A pipeline example&lt;br /&gt;
#* Input redirection with &amp;lt;code&amp;gt;&amp;amp;lt;&amp;lt;/code&amp;gt;&lt;br /&gt;
# Make the script executable and run it.&lt;br /&gt;
# Clean up all test files.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable E:&amp;#039;&amp;#039;&amp;#039; Provide the captured stderr contents and complete script output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;command-substitution&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Command Substitution ===&lt;br /&gt;
&lt;br /&gt;
Command substitution captures a command&amp;#039;s stdout for use elsewhere:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;current_date=$(date +%Y-%m-%d)&lt;br /&gt;
echo &amp;quot;Today is $current_date&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bash executes the command in &amp;lt;code&amp;gt;$()&amp;lt;/code&amp;gt;, captures its output, and substitutes it in place.&lt;br /&gt;
&lt;br /&gt;
Common in loops:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;for i in $(seq 1 10); do&lt;br /&gt;
    echo &amp;quot;$i&amp;quot;&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The &amp;lt;code&amp;gt;seq&amp;lt;/code&amp;gt; output (numbers 1-10, one per line) is captured, word-split, and becomes the loop&amp;#039;s value list.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;functions&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Functions ===&lt;br /&gt;
&lt;br /&gt;
Functions group commands for reuse:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;greet() {&lt;br /&gt;
    echo &amp;quot;Hello, $1&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
greet &amp;quot;Alice&amp;quot;    # Outputs: Hello, Alice&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Arguments:&amp;#039;&amp;#039;&amp;#039; Access via &amp;lt;code&amp;gt;$1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;$2&amp;lt;/code&amp;gt;, etc. &amp;lt;code&amp;gt;$@&amp;lt;/code&amp;gt; expands to all arguments; &amp;lt;code&amp;gt;$#&amp;lt;/code&amp;gt; gives the count.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Return values:&amp;#039;&amp;#039;&amp;#039; Functions return exit status (0-255) via &amp;lt;code&amp;gt;return&amp;lt;/code&amp;gt;. For other values, write to stdout and capture with command substitution:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;get_uppercase() {&lt;br /&gt;
    echo &amp;quot;$1&amp;quot; | tr &amp;#039;[:lower:]&amp;#039; &amp;#039;[:upper:]&amp;#039;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
result=$(get_uppercase &amp;quot;hello&amp;quot;)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;variable-scope&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Variable Scope ====&lt;br /&gt;
&lt;br /&gt;
By default, variables are global. Use &amp;lt;code&amp;gt;local&amp;lt;/code&amp;gt; for function-local variables:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;calculate() {&lt;br /&gt;
    local temp=$1&lt;br /&gt;
    local result=$((temp * 2))&lt;br /&gt;
    echo &amp;quot;$result&amp;quot;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Without &amp;lt;code&amp;gt;local&amp;lt;/code&amp;gt;, these variables would affect the global scope.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;arrays&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Arrays ===&lt;br /&gt;
&lt;br /&gt;
Arrays are collections of strings:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;files=(&amp;quot;file1.txt&amp;quot; &amp;quot;file 2.txt&amp;quot; &amp;quot;file3.txt&amp;quot;)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Access elements:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echo &amp;quot;${files[0]}&amp;quot;           # First element&lt;br /&gt;
echo &amp;quot;${files[@]}&amp;quot;           # All elements&lt;br /&gt;
echo &amp;quot;${#files[@]}&amp;quot;          # Number of elements&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Critical syntax:&amp;#039;&amp;#039;&amp;#039; Use &amp;lt;code&amp;gt;&amp;amp;quot;${array[@]}&amp;amp;quot;&amp;lt;/code&amp;gt; (with quotes) to preserve each element as a separate string:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;for file in &amp;quot;${files[@]}&amp;quot;; do&lt;br /&gt;
    echo &amp;quot;Processing: $file&amp;quot;&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This correctly processes three files, including &amp;lt;code&amp;gt;&amp;amp;quot;file 2.txt&amp;amp;quot;&amp;lt;/code&amp;gt; as one item.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Without quotes:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;for file in ${files[@]}; do    # WRONG&lt;br /&gt;
    echo &amp;quot;Processing: $file&amp;quot;&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Word splitting breaks &amp;lt;code&amp;gt;&amp;amp;quot;file 2.txt&amp;amp;quot;&amp;lt;/code&amp;gt; into &amp;lt;code&amp;gt;&amp;amp;quot;file&amp;amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;&amp;amp;quot;2.txt&amp;amp;quot;&amp;lt;/code&amp;gt;, processing four items instead of three.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;defensive-scripting-set--euo-pipefail&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Defensive Scripting: &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Bash scripts, by default, continue after errors. This can cause silent failures that are hard to debug. These options make scripts safer:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;set--e-exit-on-error&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;set -e&amp;lt;/code&amp;gt; (Exit on Error) ====&lt;br /&gt;
&lt;br /&gt;
Exit immediately if any command returns non-zero:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;set -e&lt;br /&gt;
&lt;br /&gt;
cp important.txt backup.txt&lt;br /&gt;
process backup.txt    # Won&amp;#039;t run if cp failed&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
To allow specific failures:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -f /tmp/file.txt || true&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;set--u-error-on-undefined-variables&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;set -u&amp;lt;/code&amp;gt; (Error on Undefined Variables) ====&lt;br /&gt;
&lt;br /&gt;
Treat undefined variables as errors:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;set -u&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;$undefined_var&amp;quot;    # Error: undefined_var: unbound variable&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This catches typos and logic errors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;set--o-pipefail-pipeline-failure&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;set -o pipefail&amp;lt;/code&amp;gt; (Pipeline Failure) ====&lt;br /&gt;
&lt;br /&gt;
Normally, a pipeline&amp;#039;s exit status is the last command&amp;#039;s status:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;grep &amp;quot;ERROR&amp;quot; missing.log | head -n 10&lt;br /&gt;
# Returns 0 (head succeeded) even though grep failed&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
With &amp;lt;code&amp;gt;pipefail&amp;lt;/code&amp;gt;, the pipeline fails if any command fails:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;set -o pipefail&lt;br /&gt;
&lt;br /&gt;
grep &amp;quot;ERROR&amp;quot; missing.log | head -n 10&lt;br /&gt;
# Returns non-zero (grep failed)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;recommended-practice&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Recommended Practice ====&lt;br /&gt;
&lt;br /&gt;
Start every script with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;#!/bin/bash&lt;br /&gt;
set -euo pipefail&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
These options prevent silent errors and make debugging easier.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;scripting-challenges&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Scripting Challenges ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;challenge-1-log-file-analyzer&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Challenge 1: Log File Analyzer ===&lt;br /&gt;
&lt;br /&gt;
Write &amp;lt;code&amp;gt;/tmp/lab5_log_analyzer.sh&amp;lt;/code&amp;gt; that analyzes a log file.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Requirements:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Accept one argument: log file path&lt;br /&gt;
* Print usage message if wrong number of arguments&lt;br /&gt;
* Print error if file doesn&amp;#039;t exist or isn&amp;#039;t readable&lt;br /&gt;
* Count lines containing &amp;amp;quot;ERROR&amp;amp;quot;, &amp;amp;quot;WARN&amp;amp;quot;, and &amp;amp;quot;INFO&amp;amp;quot;&lt;br /&gt;
* Print summary report&lt;br /&gt;
* Use: shebang, &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt;, proper quoting, &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt; statements, &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;, counting method&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test data:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;gt; /tmp/lab5_test.log &amp;lt;&amp;lt;&amp;#039;EOF&amp;#039;&lt;br /&gt;
2024-01-15 10:00:00 INFO Application started&lt;br /&gt;
2024-01-15 10:05:23 INFO User login successful&lt;br /&gt;
2024-01-15 10:12:45 WARN Connection timeout, retrying&lt;br /&gt;
2024-01-15 10:15:00 ERROR Database connection failed&lt;br /&gt;
2024-01-15 10:15:30 INFO Connection restored&lt;br /&gt;
2024-01-15 10:20:00 ERROR Invalid configuration parameter&lt;br /&gt;
2024-01-15 10:25:00 WARN Disk space low&lt;br /&gt;
2024-01-15 10:30:00 INFO Backup completed successfully&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Log Analysis Report for /tmp/lab5_test.log&lt;br /&gt;
==========================================&lt;br /&gt;
ERROR: 2&lt;br /&gt;
WARN:  2&lt;br /&gt;
INFO:  4&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;chmod +x /tmp/lab5_log_analyzer.sh&lt;br /&gt;
/tmp/lab5_log_analyzer.sh /tmp/lab5_test.log&lt;br /&gt;
/tmp/lab5_log_analyzer.sh&lt;br /&gt;
/tmp/lab5_log_analyzer.sh /nonexistent.log&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable Challenge 1:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Complete script with comments&lt;br /&gt;
* Output from test log&lt;br /&gt;
* Output with no arguments (usage)&lt;br /&gt;
* Output with nonexistent file (error)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;challenge-2-batch-file-processor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Challenge 2: Batch File Processor ===&lt;br /&gt;
&lt;br /&gt;
Write &amp;lt;code&amp;gt;lab5_batch_processor.sh&amp;lt;/code&amp;gt; that processes multiple files.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Requirements:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Accept two arguments: directory path and operation (&amp;amp;quot;count&amp;amp;quot;, &amp;amp;quot;uppercase&amp;amp;quot;, or &amp;amp;quot;list&amp;amp;quot;)&lt;br /&gt;
* Print usage if wrong arguments or invalid operation&lt;br /&gt;
* Define three functions (one per operation):&lt;br /&gt;
** &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt;: Count lines in each &amp;lt;code&amp;gt;.txt&amp;lt;/code&amp;gt; file&lt;br /&gt;
** &amp;lt;code&amp;gt;uppercase&amp;lt;/code&amp;gt;: Create &amp;lt;code&amp;gt;.upper.txt&amp;lt;/code&amp;gt; files with uppercase content&lt;br /&gt;
** &amp;lt;code&amp;gt;list&amp;lt;/code&amp;gt;: List &amp;lt;code&amp;gt;.txt&amp;lt;/code&amp;gt; files with sizes&lt;br /&gt;
* Store files in array, use loop to process&lt;br /&gt;
* Must use: shebang, &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt;, functions with &amp;lt;code&amp;gt;local&amp;lt;/code&amp;gt;, array with &amp;lt;code&amp;gt;&amp;amp;quot;${array[@]}&amp;amp;quot;&amp;lt;/code&amp;gt;, loop, case/if for operation selection&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test data:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;mkdir -p /tmp/lab5_batch_test&lt;br /&gt;
echo -e &amp;quot;line 1\nline 2\nline 3&amp;quot; &amp;gt; /tmp/lab5_batch_test/file1.txt&lt;br /&gt;
echo -e &amp;quot;hello world\ntest file&amp;quot; &amp;gt; /tmp/lab5_batch_test/file2.txt&lt;br /&gt;
echo -e &amp;quot;single line&amp;quot; &amp;gt; /tmp/lab5_batch_test/file3.txt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output for &amp;amp;quot;count&amp;amp;quot;:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Processing files in /tmp/lab5_batch_test&lt;br /&gt;
Operation: count&lt;br /&gt;
========================================&lt;br /&gt;
file1.txt: 3 lines&lt;br /&gt;
file2.txt: 2 lines&lt;br /&gt;
file3.txt: 1 lines&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;chmod +x lab5_batch_processor.sh&lt;br /&gt;
./lab5_batch_processor.sh /tmp/lab5_batch_test count&lt;br /&gt;
./lab5_batch_processor.sh /tmp/lab5_batch_test uppercase&lt;br /&gt;
cat /tmp/lab5_batch_test/file1.upper.txt&lt;br /&gt;
./lab5_batch_processor.sh /tmp/lab5_batch_test list&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable Challenge 2:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Complete script&lt;br /&gt;
* Output from &amp;amp;quot;count&amp;amp;quot; operation&lt;br /&gt;
* Output from &amp;amp;quot;uppercase&amp;amp;quot; operation + contents of one &amp;lt;code&amp;gt;.upper.txt&amp;lt;/code&amp;gt; file&lt;br /&gt;
* Output from &amp;amp;quot;list&amp;amp;quot; operation&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;reference-common-patterns&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Reference: Common Patterns ==&lt;br /&gt;
&lt;br /&gt;
Quick reference for common bash patterns:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Script header:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;#!/bin/bash&lt;br /&gt;
set -euo pipefail&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Check arguments:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;if [[ $# -lt 1 ]]; then&lt;br /&gt;
    echo &amp;quot;Usage: $0 &amp;lt;arg&amp;gt;&amp;quot; &amp;gt;&amp;amp;2&lt;br /&gt;
    exit 1&lt;br /&gt;
fi&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test file types:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;if [[ -f &amp;quot;$file&amp;quot; ]]; then&lt;br /&gt;
    echo &amp;quot;Regular file&amp;quot;&lt;br /&gt;
elif [[ -d &amp;quot;$file&amp;quot; ]]; then&lt;br /&gt;
    echo &amp;quot;Directory&amp;quot;&lt;br /&gt;
fi&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Read file line-by-line:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;while read -r line; do&lt;br /&gt;
    echo &amp;quot;$line&amp;quot;&lt;br /&gt;
done &amp;lt; &amp;quot;$filename&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Iterate over files:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;for file in *.txt; do&lt;br /&gt;
    [[ -f &amp;quot;$file&amp;quot; ]] || continue&lt;br /&gt;
    echo &amp;quot;$file&amp;quot;&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Command substitution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;date=$(date +%Y-%m-%d)&lt;br /&gt;
count=$(wc -l &amp;lt; &amp;quot;$file&amp;quot;)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Arrays:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;arr=(&amp;quot;item1&amp;quot; &amp;quot;item 2&amp;quot; &amp;quot;item3&amp;quot;)&lt;br /&gt;
for item in &amp;quot;${arr[@]}&amp;quot;; do&lt;br /&gt;
    echo &amp;quot;$item&amp;quot;&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Functions:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;process() {&lt;br /&gt;
    local file=&amp;quot;$1&amp;quot;&lt;br /&gt;
    echo &amp;quot;Processing $file&amp;quot;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverables-and-assessment&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Deliverables and Assessment ==&lt;br /&gt;
&lt;br /&gt;
Submit a single document (PDF or similar) containing screenshots of:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Exercise Deliverables:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Exercise A:&amp;#039;&amp;#039;&amp;#039; Permission outputs, error message, successful output, explanation&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Exercise B:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; outputs, script + pwd output, explanation&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Exercise C:&amp;#039;&amp;#039;&amp;#039; Error/success outputs, script output, explanation&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Exercise D:&amp;#039;&amp;#039;&amp;#039; Exit status values, script output, explanation&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Exercise E:&amp;#039;&amp;#039;&amp;#039; Stderr contents, script output, FD explanations&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Challenge Deliverables:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Challenge 1:&amp;#039;&amp;#039;&amp;#039; Script code, test outputs (success, no args, nonexistent file)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Challenge 2:&amp;#039;&amp;#039;&amp;#039; Script code, outputs for all three operations, &amp;lt;code&amp;gt;.upper.txt&amp;lt;/code&amp;gt; contents&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;additional-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Additional Resources ==&lt;br /&gt;
&lt;br /&gt;
This lab provides foundations for the next exercise on signals and inter-process communication. You&amp;#039;ll use bash&amp;#039;s &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt; builtin and named pipes without writing C code.&lt;br /&gt;
&lt;br /&gt;
For further study:&lt;br /&gt;
&lt;br /&gt;
* Bash manual: &amp;lt;code&amp;gt;man bash&amp;lt;/code&amp;gt;&lt;br /&gt;
* ShellCheck: https://www.shellcheck.net/&lt;br /&gt;
* Advanced Bash-Scripting Guide: https://tldp.org/LDP/abs/html/&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
</feed>