mirror of
https://github.com/ellysh/bash-programming-from-scratch.git
synced 2026-01-12 08:21:52 +00:00
Minor fixes
This commit is contained in:
@ -26,7 +26,7 @@ I> The Bash shell has good integration with Linux and macOS. You can access most
|
||||
|
||||
If the OS fails, you need system utilities to recover it. They have a command-line interface because a GUI often is not available after the failure.
|
||||
|
||||
Besides the administration tasks, you would need CLI when [connecting computers over a network](https://en.wikipedia.org/wiki/Remote_desktop_software). There are GUI programs for such connection. The examples are TeamViewer and Remote Desktop. They require a stable and fast network connection for working well. If the connection is not reliable, these programs are slow and fail often. The command interface does not have such a limitation. The remote server receives your command even if the link is poor.
|
||||
Besides the administration tasks, you would need CLI when [connecting computers over a network](https://en.wikipedia.org/wiki/Remote_desktop_software). There are GUI programs for such connection. The examples are TeamViewer and Remote Desktop. They require a stable and fast network connection for working well. If the connection is not reliable, the GUI programs are slow and often fail. The command interface does not have such a limitation. The remote server receives your command even if the link is poor.
|
||||
|
||||
You can say that a regular user does not deal with administration tasks and network connections. Even if you do not have such tasks, using command shell speeds up your daily work with the computer. Here are few things that you can do more effective with CLI than with GUI:
|
||||
|
||||
@ -47,6 +47,8 @@ There are special programs to edit and compile source code. Such programs are ca
|
||||
|
||||
If you are an experienced programmer, knowing the CLI encourages you to develop helper utilities. It happens because writing a program with a command interface is much faster than with a GUI. The speed of development is essential when solving one-off tasks.
|
||||
|
||||
Here is an example situation when you would need to write a helper utility. Suppose that you have to make a massive change in the source code of your project. You can do it with IDE by repeating the same action many times. Another option is to spend time writing a utility that will do this job. You should compare the required time for both ways of solving your task. If you are going to write a GUI helper utility, it takes more time than for a CLI utility. This can lead you to the wrong decision to solve the task manually using the IDE. Automating your job is the best option in most cases. It saves your time and helps to avoid mistakes.
|
||||
Here is an example situation when you would need to write a helper utility. Suppose that you have to make a massive change in the source code of your project. You can do it with IDE by repeating the same action many times.
|
||||
|
||||
You decide if you need to learn the CLI. I have only given few examples when it is beneficial. It is hard to switch from using a GUI to a CLI. You have to re-learn many things that you do with Windows Explorer regularly. But once you get the hang of the command shell, your new productivity will surprise you.
|
||||
Another option is to spend time writing a utility that will do this job. You should compare the required time for both ways of solving your task. If you are going to write a GUI helper utility, it takes more time than for a CLI utility. This can lead you to the wrong decision to solve the task manually using the IDE. Automating your job is the best option in most cases. It saves your time and helps to avoid mistakes.
|
||||
|
||||
You decide if you need to learn the CLI. I have only given few examples of when it is beneficial. It is hard to switch from using a GUI to a CLI. You have to re-learn many things that you do with Windows Explorer regularly. But once you get the hang of the command shell, your new productivity will surprise you.
|
||||
|
||||
@ -16,7 +16,7 @@ There is an address bar at the top of the Windows Explorer window. It displays t
|
||||
|
||||
Another way to specify the file system object place is using the **relative path**. It shows you how to reach the object from the current directory.
|
||||
|
||||
A [directory](https://en.wikipedia.org/wiki/Directory_(computing)) is a file system cataloging structure. It can contain references to files and other directories. Windows terminology calls it [**folder**](https://en.wikipedia.org/wiki/Directory_(computing)#Folder_metaphor). Both names mean the same kind of file system object.
|
||||
A [directory](https://en.wikipedia.org/wiki/Directory_(computing)) is a file system cataloging structure. It can contain files and other directories. Windows terminology calls it [**folder**](https://en.wikipedia.org/wiki/Directory_(computing)#Folder_metaphor). Both names mean the same kind of file system object.
|
||||
|
||||
Figure 2-6 shows an Explorer window. The address bar equals `This PC > Local Disk (C:) > msys64` there. It matches the `C:\msys64` absolute path. Thus, we see the contents of the `msys64` directory on the `C` drive in the Explorer window.
|
||||
|
||||
@ -43,7 +43,7 @@ You can connect extra disk drives to your computer. Another option is to split a
|
||||
|
||||
The [File Allocation Table](https://en.wikipedia.org/wiki/File_Allocation_Table) (FAT) file system dictates how Windows manages disks and provides you access to them. Microsoft developed this file system for the [MS-DOS](https://en.wikipedia.org/wiki/MS-DOS) OS. The principles of FAT became the basis of the [ECMA-107](http://www.ecma-international.org/publications/standards/Ecma-107.htm) standard. The next-generation file system from Microsoft is called [NTFS](https://en.wikipedia.org/wiki/NTFS). It replaced the obsolete FAT in modern versions of Windows. However, the basic principles of disks and directory structure are the same in NAT and FAT. The reason for that is the backward compatibility requirement.
|
||||
|
||||
The Unix directory structure follows the [POSIX standard](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap10.html#tag_10). This structure is more limited and strict than the Windows one. It has several predefined directories that you cannot move or rename. You are allowed to put your data in the specific paths only.
|
||||
The Unix directory structure follows the [POSIX standard](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap10.html#tag_10). This structure gives you less freedom than the Windows one. It has several predefined directories that you cannot move or rename. You are allowed to put your data in the specific paths only.
|
||||
|
||||
The POSIX standard says that the file system should have a top-level directory. It is called the [**root directory**](https://en.wikipedia.org/wiki/Root_directory). The slash sign / denotes it. All directories and files of all connected disk drives are inside the root directory.
|
||||
|
||||
@ -92,11 +92,11 @@ Here is the last point to mention regarding Unix and Windows file systems. Use t
|
||||
|
||||
We are ready to learn our first Bash commands. Here are the steps to execute a shell command:
|
||||
|
||||
1. Make the terminal window active.
|
||||
2. Type the command.
|
||||
1. Open the terminal window.
|
||||
2. Type the command there.
|
||||
3. Press Enter.
|
||||
|
||||
The shell will process your input.
|
||||
The shell will execute your command.
|
||||
|
||||
When the shell is busy, it cannot process your input. You can distinguish the shell's state by the [**command prompt**](https://en.wikipedia.org/wiki/Command-line_interface#Command_prompt). It is a sequence of one or more characters. The default prompt is the dollar sign $. You can see it in Figure 2-4. If the shell prints the prompt, it is ready for executing your command.
|
||||
|
||||
@ -143,7 +143,7 @@ When you read an article about Bash on the Internet, its author can confuse the
|
||||
|
||||
#### pwd
|
||||
|
||||
Let's consider the commands in Table 2-1. We have just started the terminal. The first thing we do is to find out the current directory. You can get it from the command prompt, but it depends on your Bash configuration. You do not have this feature enabled by default in Linux and macOS.
|
||||
Let's consider the commands in Table 2-1. You have just started the terminal. The first thing you do is to find out the current directory. You can get it from the command prompt, but it depends on your Bash configuration. You do not have this feature enabled by default in Linux and macOS.
|
||||
|
||||
When you start the terminal, it opens the home directory of the current user. Bash abbreviates this path by the tilde symbol ~. You see this symbol before the command prompt. Use tilde instead of the home directory absolute path. It makes your commands shorter.
|
||||
|
||||
@ -184,7 +184,7 @@ Suppose that you have just installed the MSYS2 environment. Then you launched th
|
||||
|
||||
Windows has a concept of hidden files and directories. The Unix environment also has it. Applications and OS create hidden files for their own needs. These files store configuration and temporary data.
|
||||
|
||||
Windows Explorer does not display hidden files and directories by default. Change the [Explorer settings](https://support.microsoft.com/en-us/windows/show-hidden-files-0320fe58-0117-fd59-6851-9b7f9840fdb2) to see them.
|
||||
I> Windows Explorer does not display hidden files and directories by default. Change the [Explorer settings](https://support.microsoft.com/en-us/windows/show-hidden-files-0320fe58-0117-fd59-6851-9b7f9840fdb2) to see them.
|
||||
|
||||
You can make the file hidden in Windows by changing its attribute. If you want to do the same in Unix, you should add a dot at the beginning of the filename.
|
||||
|
||||
@ -240,7 +240,7 @@ Another useful keystroke is Ctrl+C. It interrupts the currently running command.
|
||||
|
||||
#### cd
|
||||
|
||||
We have got everything about the current directory. Now we can change it. Suppose that you are looking for the Bash documentation. You can find it in the `/usr` system directory. Installed applications stores their non-executable files there. Call the `cd` command to go to the `/usr` path. Do it this way:
|
||||
You have got everything about the current directory. Now you can change it. Suppose that you are looking for the Bash documentation. You can find it in the `/usr` system directory. Installed applications stores their non-executable files there. Call the `cd` command to go to the `/usr` path. Do it this way:
|
||||
{line-numbers: false, format: Bash}
|
||||
```
|
||||
cd /usr
|
||||
@ -263,7 +263,7 @@ Now you are in the `/usr` directory. You can get a list of its subdirectories an
|
||||
cd ..
|
||||
```
|
||||
|
||||
I> In addition to `..`, there is another special path `.`. It points to the current directory. If you execute the command "cd .", nothing happens. You stay in the same place. You need the `.` path to run programs from the current directory.
|
||||
I> In addition to `..`, there is another special path `.` (dot). It points to the current directory. If you execute the command "cd .", nothing happens. You stay in the same place. You need the `.` path to run programs from the current directory.
|
||||
|
||||
Come back to the `/usr` directory. Then run the `ls` utility there. It will show you the `share` subdirectory. Come to this directory and call `ls` again. You will find the `doc` directory there. It contains Bash documentation. Call the `cd` command this way to reach the documentation:
|
||||
{line-numbers: false, format: Bash}
|
||||
@ -411,7 +411,9 @@ What of the following lines corresponds to the pattern "*/doc?openssl*" ?
|
||||
* /doc/openssl
|
||||
```
|
||||
|
||||
Let's apply glob patterns into practice. Suppose that you do not know the location of the Bash `README` file. You should use the `find` utility in this case. Start searching with the utility from the root directory. Now you need a search condition. It is a common practice to store documentation in directories called `doc` in Unix. Therefore, you can search files in these directories only. This way, you get the following `find` call:
|
||||
Let's apply glob patterns into practice. Suppose that you do not know the Bash `README` file location and looking for it. Then you should use the `find` utility.
|
||||
|
||||
Start searching with the utility from the root directory. Now you need a search condition. It is a common practice to store documentation in directories called `doc` in Unix. Therefore, you can search files in these directories only. This way, you get the following `find` call:
|
||||
{line-numbers: false, format: Bash}
|
||||
```
|
||||
find / -path */doc/*
|
||||
@ -484,7 +486,7 @@ Table 2-4 shows the `find` options that specify actions.
|
||||
| | | |
|
||||
| `-delete`| Delete each of the found files. The utility deletes empty directories only. | `find -name README -type f -delete` |
|
||||
|
||||
Table 2-4 shows that there are two variants of the `-exec` action. They differ by the characters at the end. It can be an escaped semicolon `\;` or a plus sign +. Use the plus sign variant only if the called command handles several input parameters. You will make a mistake if the command accepts one parameter only. It will process the first found object and skip the rest.
|
||||
Table 2-4 shows that there are two variants of the `-exec` action. They differ by the character at the end. It can be an escaped semicolon `\;` or a plus sign +. Use the plus sign only if the called command handles several input parameters. You will make a mistake if the command accepts one parameter only. It will process the first found object and skip the rest.
|
||||
|
||||
Let's apply the `-exec` action in practice. Suppose that you want to copy files with the Bash documentation into the home directory. You are interested in the HTML files only.
|
||||
|
||||
|
||||
@ -70,9 +70,9 @@ If you use macOS, you have everything to launch Bash too. Here are the steps for
|
||||
|
||||
Bash shell is not a regular GUI application. It even does not have its own window. When you run the `msys2.exe` file, it opens a window of the terminal emulator program.
|
||||
|
||||
An [**emulator**](https://en.wikipedia.org/wiki/Emulator) is a program that simulates the behavior of another program, OS or device. The emulator solves the compatibility task. For example, you want to run a Windows program on Linux. There are several ways to do that. One option is using the emulator of the Windows environment for Linux. It is called [Wine](https://en.wikipedia.org/wiki/Wine_(software)). Wine provides its own version of the Windows system libraries. When you run your program, it uses these libraries and supposes that it works on Windows.
|
||||
An [**emulator**](https://en.wikipedia.org/wiki/Emulator) is a program that mimics the behavior of another program, OS or device. The emulator solves the compatibility task. For example, you want to run a Windows program on Linux. There are several ways to do that. One option is using the emulator of the Windows environment for Linux. It is called [Wine](https://en.wikipedia.org/wiki/Wine_(software)). Wine provides its own version of the Windows system libraries. When you run your program, it uses these libraries and supposes that it works on Windows.
|
||||
|
||||
The terminal emulator solves the compatibility task too. Command-line programs are designed to work through a terminal device. Nobody uses such devices today. Cheap personal computers and laptops have replaced them. However, there are still many programs that require a terminal for working. You can run them using the terminal emulator. It uses the shell to pass data to the program. When the program returns some result, the shell receives them and passes to the terminal emulator. Then the emulator displays the results on the screen.
|
||||
The terminal emulator solves the compatibility task too. Command-line programs are designed to work through a terminal device. Nobody uses such devices today. Cheap personal computers and laptops have replaced them. However, there are still many programs that require a terminal for working. You can run them using the terminal emulator. It uses the shell to pass data to the program. When the program returns some results, the shell passes them to the terminal emulator. Then the emulator displays the results on the screen.
|
||||
|
||||
Figure 2-5 explains the interaction between input/output devices, the terminal emulator, the shell and the command-line program.
|
||||
|
||||
|
||||
@ -176,7 +176,7 @@ I> Most calculator applications can convert numeral systems. If you use Windows,
|
||||
|
||||
Why do programmers use both binary and hexadecimal numeral systems? It is more convenient to use only one of them, right? We should know more about computer hardware to answer this question.
|
||||
|
||||
The binary numeral system and [**Boolean algebra**](https://en.wikipedia.org/wiki/Boolean_algebra) are the basis of [**digital electronics**](https://en.wikipedia.org/wiki/Digital_electronics). An electrical [**signal**](https://en.wikipedia.org/wiki/Signal#Digital_signal) is the smallest portion of the information in digital electronics. When you work with such signals, you need a way to encode them. Encoding means associating specific numbers with the signal states. The signal has two states only. It can present or absent. Therefore, the simplest way to encode the signal is to take the first two integers: zero and one. Then you use zero when there is no signal. Otherwise, you use the number one. Such encoding is very compact. You can use one bit to encode one signal.
|
||||
The binary numeral system and [**Boolean algebra**](https://en.wikipedia.org/wiki/Boolean_algebra) are the basis of [**digital electronics**](https://en.wikipedia.org/wiki/Digital_electronics). An electrical [**signal**](https://en.wikipedia.org/wiki/Signal#Digital_signal) is the smallest portion of the information there. When you work with such signals, you need a way to encode them. Encoding means associating specific numbers with the signal states. The signal has two states only. It can present or absent. Therefore, the simplest way to encode the signal is to take the first two integers: zero and one. Then you use zero when there is no signal. Otherwise, you use the number one. Such encoding is very compact. You can use one bit to encode one signal.
|
||||
|
||||
The basic element of digital electronics is a [**logic gate**](https://en.wikipedia.org/wiki/Logic_gate). It converts electrical signals. You can implement the logic gate using various physical devices. Examples of such devices are an electromagnetic relay, vacuum tube and transistor. Each device has its own physics in the background. However, they work in the same way in terms of signal processing. This processing contains two steps:
|
||||
|
||||
@ -253,9 +253,17 @@ The compiler produces intermediate **object files**. The linker takes them and c
|
||||
|
||||
Why do you need two steps for compiling the source code? In theory, you can combine the compiler and linker into a single application. However, such a solution has several problems.
|
||||
|
||||
The limited RAM size causes the first problem. There is a common practice to split source code into several files. Each file matches a separate part of the program that solves the specific task. This way, you simplify your work with the source code. The compiler processes these files separately. It produces an object file for each source code file. They store the intermediate results of compilation. If you combine the compiler and linker into one application, there is no chance to save the intermediate results to the disk. It means you should compile the whole program at once. If you deal with a big program, the compilation process consumes all your RAM and crashes.
|
||||
The limited RAM size causes the first problem. There is a common practice to split source code into several files. Each file matches a separate part of the program that solves the specific task. This way, you simplify your work with the source code. The compiler processes these files separately. It produces an object file for each source code file. They store the intermediate results of compilation.
|
||||
|
||||
The second problem of the compiler-linker application is resolving **dependencies**. There are blocks of commands that call each other in the source code. Such references are called dependencies. Tracking them is the linker task. However, if you combine compiler and linker, you need extra passes through the whole program source code for resolving dependencies. The compiler needs much more time for a single pass over the source code than the linker needs it for object files. Therefore, when you have the compiler and linker separated, you speed up the overall compilation process.
|
||||
If you combine the compiler and linker into one application, storing the intermediate results on the disk becomes a controversial decision. If you do it, you get the bottleneck because of slow disk operations. In theory, you can keep all data in RAM and get better performance. However, you cannot do it. When you deal with a big program, the compilation process consumes all your RAM and crashes.
|
||||
|
||||
Suppose that you have a combined compiler-linker that stores temporary files on the disk. In this case, storing temporary files brings the performance overhead. At the same time, you do not get any benefits from it. You avoided the RAM limitation this way. However, you can get the benefit by splitting the compiler and linker. Then you simplify both applications and make it cheaper to support them. The developers of compilers chose this way.
|
||||
|
||||
The second problem of the compiler-linker application is resolving dependencies. There are blocks of commands that call each other in the source code. Such references are called **dependencies**. Tracking them is the linker task.
|
||||
|
||||
When the compiler produces the object files, they contain machine code but not the source code. It is simpler for the linker to track dependencies in the machine code.
|
||||
|
||||
If you combine compiler and linker, you need extra passes through the whole program source code for resolving dependencies. The compiler needs much more time for a single pass over the source code than the linker for processing the machine code. Therefore, when you have the compiler and linker separated, you speed up the overall compilation process.
|
||||
|
||||
The program can call blocks of commands from the library. The linker process the library file together with the object files of your program in this case. The compiler cannot process the library. Its file contains machine code but not the source code. Therefore, the compiler does not understand it. Splitting the compilation into two steps resolves the task of using libraries too.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user