1
0
Fork 0
mirror of https://codeberg.org/Mo8it/How_To_Linux.git synced 2024-10-18 09:42:38 +00:00

Call it a day

This commit is contained in:
Mo 2023-08-15 05:23:53 +02:00
parent c30081a00d
commit bb03656c61
17 changed files with 399 additions and 104 deletions

View file

@ -7,9 +7,9 @@ mod system_update;
mod zombie;
mod zombie_nuked;
use collective_score_client::check::RunnableCheck;
use collective_score_client::check::Task;
pub fn tasks() -> [(&'static str, Box<dyn RunnableCheck>); 8] {
pub fn tasks() -> [Task; 8] {
[
collective_score_intro::task(),
initial_house::task(),

74
cs/src/day2/cargo.rs Normal file
View file

@ -0,0 +1,74 @@
use collective_score_client::{
check::{Check, IntoTask, Task},
validator::{
command::{Command, CommandStatus, StdioVariant},
file::FileContent,
string_content::StringContent,
},
};
pub fn task() -> Task {
let fish_history = dirs::home_dir()
.expect("Failed to get the home directory!")
.join(".local/share/fish/fish_history");
[
Check::builder()
.description("Checking that `cargo-update` is installed")
.validator(
CommandStatus::builder()
.command(
Command::builder()
.program("which")
.args(vec!["cargo-install-update".into()])
.stdout(StdioVariant::Null)
.stderr(StdioVariant::Null)
.stdin(StdioVariant::Null)
.build(),
)
.status_code(0)
.build(),
)
.hint("Did you install `cargo-update` using `cargo`?")
.build()
.into_box(),
Check::builder()
.description("Checking that `tldr` is installed")
.validator(
CommandStatus::builder()
.command(
Command::builder()
.program("which")
.args(vec!["tldr".into()])
.stdout(StdioVariant::Null)
.stderr(StdioVariant::Null)
.stdin(StdioVariant::Null)
.build(),
)
.status_code(0)
.build(),
)
.hint("Did you install `tealdeer` using `cargo`?")
.build()
.into_box(),
Check::builder()
.description("Checking that you ran tldr")
.validator(FileContent {
file: fish_history.clone(),
expected: StringContent::Part("\n- cmd: tldr dnf\n"),
})
.hint("Did check the command `dnf` using `tldr`?")
.build()
.into_box(),
Check::builder()
.description("Checking that you ran tldr not only once")
.validator(FileContent {
file: fish_history.clone(),
expected: StringContent::Part("\n- cmd: tldr apt\n"),
})
.hint("Did check the command `apt` using `tldr`?")
.build()
.into_box(),
]
.into_task("cargo")
}

15
cs/src/day2/curly_wc.rs Normal file
View file

@ -0,0 +1,15 @@
use collective_score_client::{
check::{Check, IntoTask, Task},
validator::{stdin::Stdin, string_content::StringContent},
};
pub fn task() -> Task {
Check::builder()
.description("Checking the number of characters")
.validator(Stdin {
expected: StringContent::Full("756\n"),
})
.hint("curl … | … | cs task curly-wc")
.build()
.into_task("curly-wc")
}

View file

@ -0,0 +1,17 @@
use collective_score_client::{
check::{Check, IntoTask, Task},
validator::{file::FileContent, string_content::StringContent},
};
pub fn task() -> Task {
Check::builder()
.description("Checking you disabled the Fish greeting")
.validator(FileContent {
file: dirs::home_dir()
.expect("Failed to get the home directory!")
.join(".config/fish/config.fish"),
expected: StringContent::Part("set -g fish_greeting"),
})
.build()
.into_task("fish-config")
}

View file

@ -0,0 +1,20 @@
use collective_score_client::{
check::{Check, IntoTask, Task},
validator::{file::FileContent, string_content::StringContent},
};
pub fn task() -> Task {
Check::builder()
.description("Checking you made Fish the default Zellij shell")
.validator(FileContent {
file: dirs::home_dir()
.expect("Failed to get the home directory!")
.join(".config/zellij/config.kdl"),
expected: StringContent::Part("default_shell \"/usr/bin/fish\""),
})
.hint(
"Did you set the option `default_shell`? Did you check the path of fish using `which`?",
)
.build()
.into_task("fish-in-zellij")
}

19
cs/src/day2/mod.rs Normal file
View file

@ -0,0 +1,19 @@
mod cargo;
mod curly_wc;
mod fish_config;
mod fish_in_zellij;
mod pdf;
mod redirections;
use collective_score_client::check::Task;
pub fn tasks() -> [Task; 6] {
[
cargo::task(),
fish_config::task(),
fish_in_zellij::task(),
curly_wc::task(),
redirections::task(),
pdf::task(),
]
}

17
cs/src/day2/pdf.rs Normal file
View file

@ -0,0 +1,17 @@
use collective_score_client::{
check::{Check, IntoTask, Task},
validator::{file::FileContent, string_content::StringContent},
};
use std::path::PathBuf;
pub fn task() -> Task {
Check::builder()
.description("Checking the downloaded PDF file")
.validator(FileContent {
file: PathBuf::from("knowunity.pdf"),
expected: StringContent::Part("Build 19E258"),
})
.hint("Did you download the PDF file?")
.build()
.into_task("pdf")
}

View file

@ -0,0 +1,43 @@
use collective_score_client::{
check::{Check, IntoTask, Task},
validator::{file::FileContent, string_content::StringContent},
};
use std::path::PathBuf;
pub fn task() -> Task {
[
Check::builder()
.description("Checking the stdout file")
.validator(FileContent {
file: PathBuf::from("normal_output.log"),
expected: StringContent::Full("OK\nThis is some random output\nunshipped hardly lip cactus appetite petticoat\n"),
})
.build()
.into_box(),
Check::builder()
.description("Checking the stderr file")
.validator(FileContent {
file: PathBuf::from("errors.log"),
expected: StringContent::Full("Just do what the tasks tells you\nbundle favored sierra ungraded uneaten passage\ncrummy worrisome nearness level stays handmade\n"),
})
.build()
.into_box(),
Check::builder()
.description("Checking the mixed file")
.validator(FileContent {
file: PathBuf::from("verbose.log"),
expected: StringContent::Full("OK\nThis is some random output\nJust do what the tasks tells you\nbundle favored sierra ungraded uneaten passage\nunshipped hardly lip cactus appetite petticoat\ncrummy worrisome nearness level stays handmade\n"),
})
.build()
.into_box(),
Check::builder()
.description("Checking the appended stdout")
.validator(FileContent {
file: PathBuf::from("dont_overwrite.txt"),
expected: StringContent::Full("spherical survey capillary relatable tameness fame\nOK\nThis is some random output\nunshipped hardly lip cactus appetite petticoat\n"),
})
.build()
.into_box(),
]
.into_task("redirections")
}

View file

@ -1,10 +1,18 @@
mod day1;
mod day2;
use collective_score_client::run;
use collective_score_client::{run, Tasks};
use std::process;
fn main() {
let tasks = day1::tasks().into_iter().collect();
let day1_tasks = day1::tasks();
let day2_tasks = day2::tasks();
let n_tasks = day1_tasks.len() + day2_tasks.len();
let tasks: Tasks = day1_tasks.into_iter().chain(day2_tasks).collect();
assert_eq!(tasks.len(), n_tasks, "Task name conflict!");
if let Err(e) = run(
tasks,

View file

@ -7,19 +7,26 @@
- [Terminal basics](day_1/terminal_basics.md)
- [Packages](day_1/packages.md)
- [Tasks](day_1/tasks.md)
- [Day 2](day_2/README.md)
- [Terminal upgrade](day_2/terminal_upgrade.md)
- [Shell glue](day_2/glue.md)
- [Shell tricks](day_2/shell_tricks.md)
- [Regex](day_2/regex.md)
- [CLIs of the day](day_2/clis_of_the_day.md)
- [Tasks](day_2/tasks.md)
<!--
- [Day 2](day_2/README.md)
- [Shell glue](day_2/glue.md)
- [Tasks](day_2/tasks.md)
- [Day 3](day_3/README.md)
- [Notes](day_3/notes.md)
- [CLIs of the day](day_3/clis_of_the_day.md)
- [Tasks](day_3/tasks.md)
- [Day 4](day_4/README.md)
- [Shell scripting](day_4/shell_scripting.md)
- [Notes](day_4/notes.md)
- [CLIs of the day](day_4/clis_of_the_day.md)
- [Tasks](day_4/tasks.md)
- [Day 5](day_5/README.md)
- [Notes](day_5/notes.md)
- [CLIs of the day](day_5/clis_of_the_day.md)
- [Tasks](day_5/tasks.md)
-->

View file

@ -87,7 +87,7 @@ Now, destroy the whole living room with the zombie in it.
🟢 Run `cs task zombie-nuked` in the house directory.
Tipps:
### Hints
- If you get lost, use `pwd`.
- If you are looking for an option but you can not remember it, use `--help`.

View file

@ -1,30 +1,43 @@
# CLIs of the day
## Cargo
## cargo
Cargo is the package manager of the programming language Rust.
You don't have program in Rust to use it.
It is listed here because you can use it to install many of the fancy command line programs written in Rust (like `zellij`, `ripgrep`, `fd-find`, `bat`, `tealdeer`, etc.).
You can install a program with cargo using the command:
```bash
# Install. openssl-devel needed for cargo-update
sudo dnf install cargo openssl-devel
cargo install PROGRAMNAME
```
You don't need to run it with `sudo` since it installs programs for the current user only.
It doesn't modify files not belonging to the user.
To update programs installed using `cargo`, you need to have `cargo-update` installed:
```bash
# The package `openssl-devel` is needed to compile `cargo-update`
sudo dnf install openssl-devel
# To be able to run cargo install-update -a
cargo install cargo-update
# Install crate (package)
cargo install CRATENAME
# Update installed crates
cargo install-update -a
```
## curl
We did use `curl`, but not yet for downloading.
We did already use `curl`, but not yet for downloading.
```bash
# Download file into current directory while using the default name
# Download a file into the current directory while keeping the default name of the file.
curl -L LINK_TO_FILE -O
# Download file while giving the path to save the file into
# Download a file while giving the path to save the file into
# (notice that we are using small o now, not O)
curl -L LINK_TO_FILE -o PATH
```
@ -33,15 +46,30 @@ curl -L LINK_TO_FILE -o PATH
## xargs
`xargs` uses each line from the standard input (stdin) as an argument to the command specified after it.
Here is an example that shows the content of all files with the extension `.txt` in the current directory.
```bash
# Show the content of all files starting with '*.sh'
find . -type f -name '*.sh' | xargs cat
ls *.txt | xargs cat
```
If you have the files `fiel1.txt` and `file2.txt` in the current directory, then the command above is equivalent to just running `cat file1.txt file2.txt`.
## ripgrep
```console
$ rg '.*,(.*),.*' -r '$1' demo.txt
```
ripgrep is like `grep`, but it offers [many additional features](https://github.com/BurntSushi/ripgrep#why-should-i-use-ripgrep) and has much better performance (+ it is written in Rust 🦀).
## jq
Here is an example of how you can use it with regex to catch a group:
```console
$ cat demo.csv
a,b,c
x,y,z
1,2,3
$ rg '.*,(.*),.*' -r '$1' demo.csv
b
y
2
```

View file

@ -1,12 +1,17 @@
# Regex
**Reg**ular **ex**pressions
[**Reg**ular **ex**pressions](https://en.wikipedia.org/wiki/Regular_expression) specify a match patter in a text.
Can be used for example with `grep`, `rg`, `find`, `fd`, `nvim`, etc.
They can be used for example with `grep`, `rg`, `find`, `fd`, `vim`, etc.
Similar expressions are also used for Git (`.gitignore`) and containerization (`.containerignore`) which we will learn about later.
Here are some of the most important building blocks:
- `^`: Start of line
- `$`: End of line
- `()`: Group
- `|`: Alternation
- `[abcd]`: Character set, here `a` until `d`
- `[a-z]`: Character range, here `a` until `z`
- `[^b-h]`: Negated character range, here `b` to `h`
@ -14,8 +19,10 @@ Can be used for example with `grep`, `rg`, `find`, `fd`, `nvim`, etc.
- `.*`: 0 or more characters
- `.+`: 1 or more characters
- `\w`: Letter or number
- `\W`: Not letter nor number
- `\W`: Neither letter nor number
- `\d`: Digit
- `\D`: Not digit
- `\s`: Whitespace
- `\S`: Not whitespace
Writing regular expressions is not easy, but there are many (online) tools that you can use to test and debug your regex.

View file

@ -1,31 +1,30 @@
# Shell tricks
We will learn about two shell features that can save you a lot of typing.
## Expansion
```bash
# mkdir -p dir/sub1 dir/sub2
mkdir -p dir/sub{1,2}
`command PREFIX{aaa,bbb}POSTFIX` is expanded to `command PREFIXaaaPOSTFIX PREFIXbbbPOSTFIX`.
It also works with more than two arguments inside the curly brackets `{}`.
# touch dir/sub1/file1.txt dir/sub1/file2.txt
touch dir/sub1/file{1,2}.txt
This is especially useful when dealing with paths.
Here are some examples:
# cp dir/sub1/file1.txt dir/sub1/file1.txt
cp dir/sub1/file1.txt{,.bak}
```
- `mkdir -p dir/sub{1,2,3}` ➡️ `mkdir -p dir/sub1 dir/sub2 dir/sub3`
- `touch dir/sub1/file{1,2}.txt` ➡️ `touch dir/sub1/file1.txt dir/sub1/file2.txt`
- `cp dir/sub1/file1.txt{,.bak}` ➡️ `cp dir/sub1/file1.txt dir/sub1/file1.txt.bak`
> **Note**: The additional extension `.bak` is sometimes used for **ba**c**k**ups.
## Globbing
```bash
# Print content of all files ending with `.sh`
cat *.sh
The globbing asterisk `*` is used for expanding to every possible path in a directory.
# Move all files visible files and directories from dir1 to dir2
mv dir1/* dir2
It is best explained using examples:
# Move all hidden files and directories from dir1 to dir2
mv dir1/.* dir2
- `cat *.sh` prints the content of all files ending with `.sh` in the current directory.
- `mv dir1/* dir2` moves all **visible** files and directories from `dir1` to `dir2`.
- `mv dir1/.* dir2` moves all **hidden** files and directories from `dir1` to `dir2`.
- `mv dir1/{,.}* dir2` expands to `mv dir1/* dir1/.* dir2` and therefore moves all _visible and hidden_ files and directories from `dir1` to `dir2`.
# Move all visible and hidden files and directories from dir1 to dir2
# mv dir1/* dir1/.* dir2
mv dir1/{,.}* dir2
```
> **Note**: Fish can expand a globbing when pressing `Tab` after an asterisk `*` (but it is not always helpful).

View file

@ -2,18 +2,18 @@
Organize the files and directories of your tasks in separate directories!
## Task: Cargo
## Task: Cargo 📦️
Use `cargo` to install the following crates:
- cargo-update
- tealdeer
- `cargo-update`
- `tealdeer`
It might take a long time to compile everything.
Add `$HOME/.cargo/bin` to your `PATH`.
`cargo-update` should be installed to be able to run `cargo install-update -a` to update all installed crates. Try running the command. But you should not find any updates since you did just install the crates.
`cargo-update` should be installed to be able to run `cargo install-update -a` to update all installed crates.
Try running the command.
But you should not find any updates since you did just install the crates.
The crate `tealdeer` provides you with the program `tldr`.
@ -24,64 +24,71 @@ tldr dnf
tldr apt
```
It should be obvious to you what `tldr` does after you run the commands. Try it with other programs than `dnf` and `apt`!
It should be obvious to you what `tldr` does after you run the commands above and read their output.
Try it with other programs than `dnf` and `apt`!
## Task: ripgrep
🟢 Run `cs task cargo`.
The following website uses a PDF file but it does not let you see download it: [https://knowunity.de/knows/biologie-neurobiologie-1c6a4647-4707-4d1b-8ffb-e7a750582921](https://knowunity.de/knows/biologie-neurobiologie-1c6a4647-4707-4d1b-8ffb-e7a750582921)
## Task: Fish configuration
Know that you are kind of a "hacker", you want to use a workaround.
Disable the default greeting by Fish everytime you start it.
Use pipes `|`, `curl`, `rg` (ripgrep) and `xargs` to parse the HTML of the website, extract the link to the PDF file and download the file.
The configuration file should be `~/.config/fish/config.fish`.
🟢 Run `cs task fish-config`.
## Task: Fish in Zellij
Configure Fish as the default Zellij shell.
🟢 Run `cs task fish-in-zellij`.
## Task: Curly line count.
Use `curl` to fetch this file:
[https://codeberg.org/mo8it/collective-score/raw/commit/4ff0cd6f871e4a17a7ecd36d7d01ca7713c11ca1/Cargo.toml](https://codeberg.org/mo8it/collective-score/raw/commit/4ff0cd6f871e4a17a7ecd36d7d01ca7713c11ca1/Cargo.toml)
You don't have to save it to disk!
Now, pipe the output of `curl` into `wc` (with some option) to count the number of characters.
🟢 Pipe the number of characters into `cs task curly-wc`.
### Hints
- `curl … | … | cs task curly-wc`
## Task: IO redirections
I placed a program called `mixed-output-generator` on your system.
If you run it, it will create the file `dont_overwrite.txt` in your current directory and output random text to both stdout and stderr.
First, run it while writing its standard output to the file `normal_output.log` and its standard error to the file `errors.log`.
After that, run it again while writing both standard output and standard error to the same file `verbose.log`.
Now, run it for the last time while _appending_ the standard output to the file `dont_overwrite.txt` that it creates.
🟢 Run `cs task redirections`.
## Task: Gimme that PDF 😈
The following website uses a PDF file but it doesn't let you download it:
[https://knowunity.de/knows/biologie-neurobiologie-1c6a4647-4707-4d1b-8ffb-e7a750582921](https://knowunity.de/knows/biologie-neurobiologie-1c6a4647-4707-4d1b-8ffb-e7a750582921)
Now that you are kind of a "hacker", you want to use a workaround 😈
Use `curl`, pipes `|`, `rg` (ripgrep) and `xargs` to parse the HTML of the web page, extract the link to the PDF file and download it.
Save the PDF file using the name `knowunity.pdf`.
The link to the PDF file starts with `https://` and ends with `.pdf`.
After that it works, write a script that asks the user for the link to a document at [knowunity.de](https://knowunity.de) and downloads the PDF file.
## Task: Cows everywhere!
🟢 Run `cs task pdf` in the same directory where the downloaded PDF file is.
[![](https://imgs.xkcd.com/comics/tar_2x.png)](https://xkcd.com/1168/)
### Hints
Download the source code of this book using `curl` as a `tar.gz` archive: [https://codeberg.org/Mo8it/How\_To\_Linux/archive/main.tar.gz](https://codeberg.org/Mo8it/How_To_Linux/archive/main.tar.gz)
We are not using Git at this point to practice dealing with archives.
Extract the files from the archive! (_Don't worry, you have more than 10 seconds_)
Use `find` to find all Markdown files (ending with `.md`). Then use `sed` to replace every match of `echo` with `cowsay` in the files found.
Why? Easy: Why not?
## Task: Parsing a CSV file
1. Use `curl` to take a look at the file with the following link: [https://gitlab.rlp.net/mobitar/julia\_course/-/raw/main/Day\_3/resources/fitting\_task\_data.csv](https://gitlab.rlp.net/mobitar/julia_course/-/raw/main/Day_3/resources/fitting_task_data.csv). The file contains measurement of a (fake) free fall experiment.
1. Now that you know what the file contains, pipe the output to tools that let you remove the first 6 and last 2 lines. Afterwards, extract the first (measured height) and third column (measured time).
1. Write a small Python that processes the output of the command from the last step. Since this book is not about programming in Python or plotting, the simple code to process the output of the variable `h_t` is given below:
```
#!/usr/bin/env python3
import matplotlib.pyplot as plt
(...)
h_values = []
t_values = []
for line in h_t.strip().split("\n"):
h, t = line.strip().split(",")
h_values.append(h)
t_values.append(t)
plt.plot(h_values, t_values)
plt.xlabel("h")
plt.ylabel("t")
plt.savefig("h_t.pdf")
```
Use the command that you did write in the last step to extract the two columns and save the output in a variable called `h_t` in the `(...)` block.
1. Save the Python script into a file in an empty directory called `bash_python_harmony`.
1. Use `poetry` to initialize an environment in the directory `bash_python_harmony`.
1. Use `poetry` to add the package `matplotlib` to the environment.
1. Use `poetry shell` to enter the environment.
1. Run the script and check the generated PDF file `h_t.pdf`.
- To capture a regex group using ripgrep, you should use `rg '.*(GROUP_PATTERN).*' -r '$1'` after replacing `GROUP_PATTERN` with the pattern that you are looking for.
- If you find the link of the file using `rg`, copy it into a browser to make sure that you got a correct link to a PDF file.

View file

@ -5,7 +5,7 @@
In this task, we will learn about the power of macros in Vim.
1. Visit [this URL](https://www.randomlists.com/random-names?qty=500), select the generated 500 names with the mouse and copy them.
1. Create a new text file and open it with `nvim`.
1. Create a new text file and open it with `vim`.
1. Paste the names into the file. You should see 500 lines with a name in each line.
1. Record a macro that changes a line in the form `FIRST_NAME LAST_NAME` to `("FIRST_NAME", "LAST_NAME"),`.
1. Run the macro on all lines.
@ -53,7 +53,7 @@ We will use the program `inotifywait`. This program can monitor a directory and
1. After printing the script name, run the script!
1. Save the standard output and standard error of the script into two separate files in the `logs` directory. If the name of the script is `job.sh` for example, then the output should be in the files `logs/job.sh.out` and `logs/job.sh.err`.
##### Tipps:
### Hints
- Take a look at the examples from the sections of this day.
- Take care of permissions.
@ -111,6 +111,40 @@ Submit your job script multiple times and take a look at the terminal that is ru
Verify the redirection of the standard output and standard error in the directory `logs`.
## Task: Parsing a CSV file
1. Use `curl` to take a look at the file with the following link: [https://gitlab.rlp.net/mobitar/julia\_course/-/raw/main/Day\_3/resources/fitting\_task\_data.csv](https://gitlab.rlp.net/mobitar/julia_course/-/raw/main/Day_3/resources/fitting_task_data.csv). The file contains measurement of a (fake) free fall experiment.
1. Now that you know what the file contains, pipe the output to tools that let you remove the first 6 and last 2 lines. Afterwards, extract the first (measured height) and third column (measured time).
1. Write a small Python that processes the output of the command from the last step. Since this book is not about programming in Python or plotting, the simple code to process the output of the variable `h_t` is given below:
```
#!/usr/bin/env python3
import matplotlib.pyplot as plt
(...)
h_values = []
t_values = []
for line in h_t.strip().split("\n"):
h, t = line.strip().split(",")
h_values.append(h)
t_values.append(t)
plt.plot(h_values, t_values)
plt.xlabel("h")
plt.ylabel("t")
plt.savefig("h_t.pdf")
```
Use the command that you did write in the last step to extract the two columns and save the output in a variable called `h_t` in the `(...)` block.
1. Save the Python script into a file in an empty directory called `bash_python_harmony`.
1. Use `poetry` to initialize an environment in the directory `bash_python_harmony`.
1. Use `poetry` to add the package `matplotlib` to the environment.
1. Use `poetry shell` to enter the environment.
1. Run the script and check the generated PDF file `h_t.pdf`.
## Task: Vim game
In this task, we are going to play a game! 🎮️

View file

@ -131,7 +131,7 @@ After running the second script, you should see the container compiling and then
Now that you have the program `tmate`, find out what it does! Try it with a second person.
##### Tipps:
### Hints
- On Debian based distributions like Ubuntu, the package manager is `apt`. Before that you can install any packages with `apt`, you have to run `apt update`. This does not run system updates like `dnf upgrade`. `apt update` does only synchronize the repositories which is needed before installations.
- Test if a file exists in bash: