Unix is from the ’70s:
Unix is free to modify:
Unix becomes the GCD of computing.
Freedom at stake:
Sharing is caring:
People want to save their idea of moral responsibility by formalising it as a licenses
In 1984 RMS starts the GNU project, with the goal to provide a free Unix-compatible OS
Steps to success:
Even though this project gets mocked for hurd, it is essential for Linux’ success!
In 1992 Linus Torvalds writes his own little terminal emulator, to be able to remotely access his university account.
Later this code evolves into the Linux kernel, that together with many GNU utilities becomes GNU/Linux — a.k.a Linux
Segment | Linux variant | Market share (Source) |
---|---|---|
Desktop | Debian / Fedora / Arch / … | ~2% |
Smartphone / Tablet | Android | ~70% |
Server | Debian / RHEL / Gentoo / … / CUSTOM | ~65% |
Supercomputer | CentOS / CUSTOM | ~99% |
Mainframe | RHEL / SLES | ~30% |
Embedded | Android / … | ~30% |
You are familiar with either:
When something breaks, you:
by a show of hands:
/
denotes the root directory/dir1/dir2/object
dir2/object
.
— is a reference to the directory itself..
— is a reference to the parent directoryThese are the helper types:
type Name = String type Children = [FSObject] type Path = [FSObject] data LinkType = Soft | Hard
This is the code describing a trivial Linux-ish filesystem in a mathematical notation:
data FSObject = File Name | Dir Name Children | Link Name Path LinkType
Missing:
The Filesystem Hierarchy Standard is a guideline for the structuring of local filesystems.
It aims for better interoperability between different Systems, by making it easy to figure out the location of any kind of file.
Directory | Purpose |
---|---|
/ | root of the hierarchy |
/bin/ | basic binary files |
/boot/ | files for boot support |
/dev/ | virtual files, for direct access to devices |
/etc/ | system-wide confidurations - historical, misleading name |
/home/ | users’ home direcories are located here |
/lib/ | library files for /bin/ /sbin |
/media/ | removable media mounts |
/mnt/ | temporary mounts |
/opt/ | optional applications |
/proc/ | system-internals accessable as files |
/root/ | home directory of root |
/run/ | variable data should be removed while booting |
/sbin/ | like /bin/ , but require special permissions |
/srv/ | data served to other systems |
/sys/ | statistics about devices |
/tmp/ | temporary files, can be removed while booting |
/usr/ | most higher level applications are inside, looks a lot like / |
/var/ | files that change often (cache/db/log/mail/…) |
Some systems don’t comply with the FHS, for various reasons.
/lib/
and /lib64/
are just symlinksThe default login screen on most distributions looks something like this:
<<< Welcome to ${DISTRO} - tty{1..6} >>> ${MACHINE_NAME} login: password:
username <blank while typing>
<HTML> <div class=“incremental”> <div> <hr style=“clear: both;”> </HTML>
remote login looks similar:
$ ssh username@remote password:
<HTML>
</div>
<div>
<hr style=“clear: both;”>
</HTML>
<HTML>
</div>
</HTML>
If the login was not successful, just try again.
Beware, that systems may be configured to lock your account if too many attempts were made.
<HTML> </div> </HTML>
This is how the prompt looks by default:
[myname@l3_ ~]$
$PS1
echo $PS1
Ways to get help when you’re stuck:
man
apropos
whatis
info
-h
/--help
flagTo execute a program, we call it:
nano file.txt ./a.out -flag=value gcc -I/home/lv12345/myuser/include module load non-existent-module
The default way to apply arguments to a program is to write a space separated list of arguments after the program when calling it.
Patterns and expansions are defining multiple arguments with little overhead:
false ; echo "Should I be Printed?" false && echo "Should I be Printed?" false || echo "Should I be Printed?"
Should I be Printed? Should I be Printed?
for i in * do mv $i{,.bak} done
while true do echo "Annoying Hello World" sleep 3 done
Or as one-liners:
for i in *; do mv $i{,.bak}; done while true; do echo "Annoying Hello World"; sleep 3; done
Whenever you have a command, which you:
you can define an alias for it:
alias ll='ls -alh' alias myProject='cd $ProjectDir; testSuite; compile && testSuite; cd -'
Your shell keeps a log of all the commands you executed.
history
command is used to access this history<CTRL>-R
keysWrite output to a file or file-descriptor
Command | Redirect | Append | Description |
---|---|---|---|
program | > std.log | >> std.log | redirect stdout to a file |
program | 2> err.log | 2>> err.log | redirect stderr to a file |
program | 2>&1 | redirect stderr to stdout |
Write output into the input-stream of another process
Command | Pipe | Description |
---|---|---|
program | | grep -i foo | pipe stdout into grep |
program | | tee file1 file2 | overwrite files and stdout |
program | | tee -a file | append to files and stdout |
LANG=de_AT.UTF-8 firefox env LANG=de_AT.UTF-8 firefox
export LANG=de_AT.UTF-8
env echo ${LANG}
unset LANG env -u LANG
Some variables that could affect you are:
$EDITOR # the default editor for the CLI $PAGER # utility to read long streams $PATH # program paths, in priority order
if you’re aiming for programming, these could be more interresting:
$LIBRARY_PATH # libraries to link by the compiler $LD_LIBRARY_PATH # libraries to link at runtime $CC # sometimes used to set default C compiler $CFLAGS # default flags for compiling C
looking around is done by thels
(list) command
$ ls # shows files and directories $ ls -a # includes hidden ones $ ls -l # detailed view
testdir test . .. testdir test drwxr-xr-x 1 myuser p12345 0 Apr 13 11:55 testdir -rw-r--r-- 1 myuser p12345 4 Apr 13 11:55 test
command | result |
---|---|
ls / | bin dev etc home sbin usr |
ls /home | user1 user2 |
moving around is done by thecd
(change directory) command
finding the current position in the filesystem is done by thepwd
(print working directory) command
$ cd /bin # go to an absolute directory $ cd [~] # go home $ cd - # go to previous directory
$ pwd # /bin $ pwd # /home/user1 $ pwd # /bin
moving files around is done by themv
(move) command
copying is done by thecp
(copy) command
$ mv old new # rename old to new $ mv old dir/ # move old into dir $ mv file1 file2 # overwrite file2 with file1 # (BEWARE)
$ cp -i input input.bak # input to input.bak $ cp -i input backup/ # input into backup $ cp -a dir1/ dir2 # exact copy of dir1
To look at everything
.txt
log_
find \ ~ \ -maxdepth 3 \ -iname "*.txt" \ -or -iname "log_*" \ -type f \ -exec cat '{}' \; | less
find \ ~ \ -maxdepth 3 \ -iname "*.txt" \ -or -iname "log_*" \ -type f \ -exec cat '{}' + | less
creating directories is done by themkdir
(make directory) command:
deleting directories is done by thermdir
(remove directory) command:
$ mkdir newdir # create directory newdir $ mkdir -p new/dir # create dir inside of new
$ rmdir emptydir # removes emptydir $ rmdir -v emptydir # removes emptydir (verbose)
creating files is done by thetouch
orecho
commands:
deleting files is done by therm
(remove) command:
$ touch file # create empty file $ echo "abc" > file # create/overwrite file
$ rm file # removes file $ rm -ri dir # removes dir recursively
viewing is done by theless
command:
concatenating is done by thecat
command:
$ less file.txt # exit with 'q' $ less -R file.txt # keep colors $ cat file1 file2 | less
$ cat file $ cat -A printable $ cat -n numbered
$ echo "VSC is great" > file $ cat file VSC is great
$ echo "VSC is awesome" >> file $ cat file VSC is great VSC is awesome
$ cat file | grep awesome VSC is awesome
$ grep awesome file VSC is awesome
viewing used space is done by thedu
(disk usage) command:
viewing free space is done by thedf
(disk free) command:
$ du -h file1 file2 # human readable output $ du -s dir # summarize
$ df -h # human readable output $ df -t nfs # only list filesystems of a type
$ mv space.log space.log.bak
$ df -h | grep "lv12345\|lv54321" > space.log
$ cat space.log
nfs05.ib.cluster:/e/lv12345 200G 185G 16G 93% /home/lv12345 nfs04.ib.cluster:/e/lv54321 1000G 979G 22G 98% /home/lv54321
we do this often, let’s wrap it up!
$ mv space.log space.log.bak
$ df -h | grep "lv12345\|lv54321" > space.log
$ cat space.log
nfs05.ib.cluster:/e/lv12345 200G 185G 16G 93% /home/lv12345 nfs04.ib.cluster:/e/lv54321 1000G 979G 22G 98% /home/lv54321
we do this often, let’s wrap it up!
$ echo '#!/bin/bash' > spacelog.sh $ echo 'mv space.log space.log.bak' >> spacelog.sh $ echo 'df -h | grep "lv12345\|lv54321" > space.log' >> spacelog.sh $ echo 'cat space.log' >> spacelog.sh
$ chmod +x spacelog.sh
$ ./spacelog.sh
sed (stream editor) and awk are powerful tools when working with the command line
$ mycommand | sed "..."
$ mycommand | awk '{...}'
Using sed and awk in action
program | command | description |
---|---|---|
sed | s/old/new/ | replace old with new |
sed | /from/,/to/ s/old/new/ | replace old with new , between from and to |
awk | 'print $5, $3' | print columns 5 and 3 of every line |
Example script:
#!/bin/bash mv space.log space.log.bak df -h | grep "lv12345\|lv54321" > space.log cat space.log
#!/bin/bash mv space.log space.log.bak df -h | grep "lv12345\|lv54321" > space.log cat space.log | sed "s|/home/lv12345|ProjectA|" \ | awk '{print $6, "free:", $4}' \ | column -t
Just to ensure, that you are able to run your scripts
Change the owner of files and directories by:
chown -R user:group dirs files
Change the mode of files and directories by:
chmod -R u=rw,g+w,o-rwx dirs files chmod -R 644 dirs files
A little test program, which we mark as executable and hand it over to the corresponding interpreter:
cat << EOF > test.sh echo "${LANG}" echo "${PATH}" EOF
chmod +x test.sh
bash test.sh
Don’t we have an OS, capable of executing everything it recognises as a executable?
Yes, we do!
cat << EOF > test.sh #!/bin/bash echo "${LANG}" echo "${PATH}" EOF
chmod +x test.sh
./test.sh
Programming in bash would be cumbersome without functions, so here we go:
printAllNumbers () { echo "1 2 3" echo "done" }
This isn’t good, as were only getting a fixed amount of numbers. let’s try a recursive approach
#!/bin/bash printAllNumbersFrom () { num=$1 echo "${num}" printAllNumbersFrom "$(($num + 1))" } printAllNumbersFrom $2
nano filename
vim filename
This editor is focused on being easy to use, but still providing every feature a user might need.
The interface consists of four parts, namely from top to bottom:
Nothing special, key-bindings visible while editing
Feature | Usage |
---|---|
Navigation | Arrow keys |
Actual editing | Typing text, as usual |
Cut/Paste line | <CTRL> +k / <CTRL> +u |
… | explained in key bindings field |
Use this editor if you are new to the command line.
It is straight forward, but can be extended on the way.
This editor is focused on productivity and efficiency, providing everything a user might need.
The simple interface consists of two parts:
Since this editor is very easy to extend, after setting up a few plugins, it will probably look quite different!
This is a multimode editor, you’ll have to switch modes whenever you change what you want to do.
Feature | Usage |
---|---|
Navigation | hjkl / Arrow keys |
Writing | change to input mode, then write as usual |
Commands | exit current mode, press : |
… | explained on next slide |
Use this editor if you like a challenge.
It is fast and very nice — but you’ll sometimes get hurt on the way.
<ESC>
<RETURN>
):w [filename]
q
q!
h [topic]
, e.g. h tutorial
%s/old/new/gc
i
:
(colon)v
<SHIFT>-v
x
dd
d
/abc
<HTML> <!–
Public domain:
- [Ubuntu Tango Icons](https://wiki.ubuntuusers.de/Wiki/Icons/Tango/)
Mixed (CC, public domain, …)
- [Wikipedia Tango Icons](https://commons.wikimedia.org/wiki/Tango_icons)
# Timers ## Systemd Units ## Systemd Timers ## Putting it together
## Background ### What is GNU/Linux {.incremental}
![](pictures/gnu.svg){height=200px style=“float: left;” .incremental} <div class=“columns”>
- It's a collection of Software, including:
- Linux itself, as the `kernel`
- basic tools, mostly from the GNU project
- additional, specialised software for most tasks
- the OS that powers the internet, the mobile world and more
- opensource and free software effort to retain essential freedoms of the user
</div>
### Reasons for Linux {.incremental}
![](pictures/tux.svg){height=200px style=“float: right;”.incremental} <div class=“columns”>
- Wide Adoption
- Stable platform
- Anyone can try and use Linux
- Easy adaption to custom environments
</div>
### Basic Usage {.incremental}
![](https://upload.wikimedia.org/wikipedia/commons/6/66/Utilities-terminal.svg){height=200px style=“float: left;” .incremental} <div class=“columns”>
- using and understanding the shell
- moving through the directory structure
- moving files and directories
- editing files
- writing and executing scripts
- environment and variables
</div>
## CLI Basics <div class=“columns”> ### Shell
The shell used on the VSC is called bash. It's the most common shell today, preinstalled by most Linux and macOS versions.
- It's behaviour is controlled by the file .bashrc
- loaded at login
- can be customized
- is a shell script
- Has some usability features
- most of the time commands can be auto-completed by pressing `TAB`
- can recall history with `CTRL-R` or `history`
- Provides a full programming language/environment
- can do native processing of Strings, Integers and Arrays
- can execute any program
- is turing-complete, but not considered a general-purpose language
### Environment
The shell and some default tools are configured through variables.
- list variables with `env`
- modify them with `export X=Y`
- get the content by prepending it with a dollar sign `$A`
- useful in scripts
<div class=“incremental”> Some variables that could affect you are:
``` $EDITOR # the default editor for the CLI $PAGER # utility to read long streams $PATH # program paths, in priority order ``` </div> </div>
## Executing Commands
<div class=“columns”> <div class=“incremental”> To execute a program, we call it:
```{.bash} nano file.txt ./a.out -flag=value gcc -I/home/lv12345/myuser/include module load non-existent-module ``` </div>
- Every command that is executed will provide a return-value on exit.
- ![](https://media-cdn.ubuntu-de.org/wiki/attachments/21/07/dialog-ok.png){height=30px} A value of `0` means success
- ![](https://media-cdn.ubuntu-de.org/wiki/attachments/39/07/dialog-cancel.png){height=30px} any other value means failure
</div>
### Program Arguments {.incremental}
<div class=“incremental”>
The default way to apply arguments to a program is to write a space separated list of arguments after the program when calling it.
Patterns and expansions are defining multiple arguments with little overhead:
</div> <div class=“columns”>
- the most important patterns are:
- ? — matches one character
- \* — matches any character sequence
- the most important expansions are:
- A{1,2}Z — expands to A1Z A2Z
- 1{a..z}A — expands to 1aA 1bA … 1zA
</div>
### Chaining Commands {.incremental}
<div class=“columns incremental”> ``` {.bash .numberLines} false ; echo “Should I be Printed?” false && echo “Should I be Printed?” false || echo “Should I be Printed?” ```
``` {.bash .numberLines} Should I be Printed?
Should I be Printed? ``` </div>
<!–
- - -
### Using Patterns and Expansions {.incremental}
<div class=“columns”>
- the most important patterns are:
- ? — matches one character
- \* — matches any character sequence
- the most important expansions are:
- A{1,2}Z — expands to A1Z A2Z
- 1{a..z}A — expands to 1aA 1bA … 1zA
</div> –> <!– ## Loops
look like this in a shell:
<div class=“columns incremental”> ```{.bash} for i in * do
mv $i{,.bak}
done ```
```{.bash} while true do
echo "Annoying Hello World" sleep 3
done ``` </div>
#### Generating parameters in bash {.incremental}
<div class=“columns incremental”> ```{.bash} i=1 while [ $i -lt 10 ] do
j=1 while [ $j -le $i ] do echo "$i $j" # Your command comes here let j+=1 done let i+=1
done unset i j ```
```{.bash} 1 1 2 1 2 2 3 1 3 2 3 3 … 9 6 9 7 9 8 9 9 ```
</div>
## Getting familiar with the CLI
<div class=“columns”>
This is how the prompt looks by default:
``` {.bash} [myname@l3_ ~]$ ```
![](src/Screenshot_vsc3_login.png){height=190px .incremental} ![](src/Screenshot_vsc3_login_prompt.png){height=190px .incremental} ![](src/Screenshot_vsc3_login_prompt2.png){height=190px .incremental} </div>
<div style=“float: left”>
#### Prompt {.incremental}
- it's “promting” you to enter a command
- tells you:
- who you are
- which computer you're on
- which directory you're in
- can be configured to show more information
- setting is in a variable called `PS1`
- default value of `$PS1`: `[\u@\h \w]\$`
- can add date, time and the output of custom commands
- see `man bash` for details about your shell
</div>
<div style=“float: right” class=“incremental”> #### In case of confusion
there are many ways of getting further:
- the `man` command
- opens the manual for a specified topic
- especially nice to find man-pages: `apropos`
- the `whatis` command
- the `info` command
- is more complex and more like a browser
- most commands support a `-h`/`–help` flag
- colleagues are often helpful
- the internet is often helpful
</div>
<div style=“float: left” class=“incremental”> ![`apropos help`](src/Screenshot_vsc3_apropos.png){width=270px .incremental} </div>
## Shell - More than a prompt
### Simple Input/Output
<div class=“columns incremental”> Any program running in the shell can by default read from the keyboard and write to the screen.
We try this with the `cat` command, which is short for concatenate.
```{.bash} cat some text # Input some text # Output <CTRL>-d ``` </div>
### Piping Input/Output
<div class=“columns incremental”> Since echoing input back to the user isn't particularly useful, we'll be echoing back filtered output.
We add a pipe, which redirects the output of the command before it to the command following it, and filter for any line containing `VSC`.
```{.bash} cat | grep VSC 1 2 3 # Input, no Output 1 VSC 3 # Input 1 VSC 3 # Output <CTRL>-d ``` </div>
### Redirecting Input/Output
<div class=“columns incremental”> Now we can write the resulting stream into a file called VSC.txt.
This is done by adding a greater-sign between the end of the command and the filename of the resulting document.
```{.bash} cat | grep VSC > VSC.txt 1 2 3 # Will not be in VSC.txt 1 VSC 3 # Will be in VSC.txt <CTRL>-d ``` <!– ```{.bash} cat VSC.txt 1 VSC 3 ``` –> <!– </div>
## Cool Tools
### Less is More
<div class=“columns”> Since piping output is awesome we want to have a program, that can handle all the output of the other programs and present it in screen-sized parts.
Hence the program `less`, which is just `more`.
```{.bash} find ~ -iname “*.txt” -type f -exec cat '{}' \; | less ``` </div>
### Permissions
<div class=“columns”> If some file can't be accessed/modified, there is a chance that you haven't got the rights to do so.
For these cases there is the tool `chmod`, which changes the mode of a file.
```{.bash} chmod a=w file chmod u+x file chmod g-w file chmod o=r file ``` </div>
### Aliasing commands
<div class=“columns”> you should define an `alias`, whenever there is a command which:
- is run with parameters which don't change between runs - has some hard to remember name - needs a non-default safeguard
```{.bash} alias f='find ~ -type f -iname' alias rm='rm -i' alias def-env='cd ~/projectA && \
module purge && \ module load x/1 y/2 z/3'
``` </div>
## Filesystem 101
<div class=“columns”>
1. Everything starts at the root
- the root is a directory
- `/` denotes the root directory
2. the filesystem has different kinds of objects
1. files
2. directories
- containers for multiple objects
3. links to objects, which either
- add a second name for the same object
- point to a position in the filesystem
3. objects can be referenced by their path
- absolute: `/dir1/dir2/object`
- relative: `dir2/object`
4. special objects in directories:
- `.` — is a reference to the directory itself
- `..` — is a reference to the parent directory
</div>
##### The Filesystem represented as an ADT {.incremental} <div class=“incremental columns”>
These are the helper types:
```{.haskell} type Name = String type Children = [FSObject] type Path = [FSObject] data LinkType = Soft | Hard ```
This is the code describing the trivial Linux-ish filesystem in a mathematical notation:
```{.haskell} data FSObject = File Name
| Dir Name Children | Link Name Path LinkType
``` </div>
- - -
### Getting and Manipulating our position in the FS
<div class=“columns”>
- `'ls'`: short for `'list'`
- `'tree'`: tree view of files and directories
- `'pwd'`: short for `'print work directory'`
- `'cd'`: short for `'change directory'`
</div>
<div class=“incremental”>
Example for looking around:
<div class=“columns incremental”> ``` $ ls # shows files and directories
$ ls -a # includes hidden ones
$ ls -l # detailed view ```
``` testdir testfile
. .. testdir testfile
drwxr-xr-x 1 myuser p12345 0 Apr 13 11:55 testdir -rw-r–r– 1 myuser p12345 4 Apr 13 11:55 testfile ``` </div> </div>
<div class=“incremental”>
Example for moving around, starting at the home directory:
</div> <div class=“columns incremental”> ``` $ pwd
$ cd ..; pwd
$ cd /global; pwd
$ cd; pwd
$ cd -; pwd ```
``` /home/lv12345/myname
/home/lv12345
/global
/home/lv12345/myname
/global ``` </div>
- - -
### Moving files and directories
<div class=“columns”>
- `'mv'`: short for `'move'`
- `'cp'`: short for `'copy'`
</div> <div class=“incremental”>
Example:
<div class=“columns”> ``` $ cp file.orig file.copy $ mv file.orig file.copy
$ cp file.orig dir $ mv file.orig dir
$ cp dir1 dir2 # ERROR $ cp -r dir1 dir2 # WORKS $ mv dir1 dir2 # WORKS ```
- copying or moving directories can result in unexpected behaviour:
- if dir2 is an existing directory, dir1 will become a subdirectory of dir2
- if dir2 is non-existent, dir1 will become dir2
</div>
<div class=“columns”> ##### Transfer to/from Cluster
- `'scp'`: short for `'secure copy'`
- arguments can be either local or remote files and direcotries
- `'rsync'`: short for `'remote syncronization'`
- similar to regular `mv`/`cp`/`scp`, except:
- pedantic about trailing slashes
- can merge directories
- can delete/overwrite changed files
</div>
`rsync` seems easy to use, but the advanced options could remove data from both sides of your transfer! It's a syncronazation utility, no simple move/copy tool </div>
<!– - - -
##### SCP
``` $ cp file.orig file.new
$ scp user@host:dir/file.orig file.new
$ scp file.orig user@host:dir/file.new ```
- this is easy to use and preferable for little, one time transfers
[ID1]: src/tux.svg “Hover Text 1” {height=150px style=“margin: 10px;”} [ID2]: src/tux.svg “Hover Text 2”
- ![Test][ID1] - ![Test](src/tux.svg “Hover Text 1”){height=150px style=“margin: 10px;”} - [<font color=#cc3300>➠</font>][ID2]
- - -
##### RSYNC
``` $ rsync dir1 dir2 # Local copy $ rsync -e ssh dir1 dir2 # Remote copy
# These two have the same result: $ rsync dir1 dir2 $ rsync dir1/ dir2/dir1
# This doesn't: $ rsync dir1/ dir2 ```
- there are many useful options, like:
- the `-e ssh` option tells rsync to connect via ssh to the remote machine
- trailing slashes have an effect on where your files will go
–> <!– ## Copyleft
Third party content and its licensing information can be found at:
- [Levitating Gnu](https://www.gnu.org/graphics/meditate.html):
- [Tux](https://pixabay.com/en/tux-penguin-animal-cute-linux-158547/):
- [Tux and Gnu](https://commons.wikimedia.org/wiki/File:GNU_and_Tux.svg):
- [Tango Icons](https://commons.wikimedia.org/wiki/Tango_icons ):
## Copyright
The not explicitly licensed or non freely-redistributable licensed stuff:
- [the Good, the Bad & the Ugly](http://cinetropolis.net/wp-content/uploads/2013/10/the-good-the-bad-and-the-ugly-t-anderson-banner.jpg):
## TestStuff 😁 $\rightarrow$ 😄 😁 $\Rightarrow$ 😄 😁 $\longrightarrow$ 😄 😁 $\Longrightarrow$ 😄
https://apps.timwhitlock.info/emoji/tables/unicode#emoji-modal
[]:#(Das ist ein Kommentar, dass nirgends aufscheint) [TuxAndGnu]: https://upload.wikimedia.org/wikipedia/commons/5/53/GNU_and_Tux.svg “Tux and Gnu” [VSC]: https://vsc.ac.at “Vienna Scientific Cluster” ## ToDo - nano/vi - df -h . - du -sh ./* - cat | sort | uniq | tr | awk | sed - example ## table head1 | head2 | head3 :—–|:—–:|—–: p1 | e11 | e12 p1 | e11 | e12e12 p1 | e12e12e12e12e12 | e12 head1 | head2 | head3 :—–|:—–:|—–: p1 | e11 | e12 p1 | e11 | e12e12 p1 | e12e12e12e12e12 | e12 –> </HTML>