337 lines
12 KiB
Markdown
337 lines
12 KiB
Markdown
## Table of Contents
|
|
1. [Bash](#bash)
|
|
* [Process priority](#process-priority)
|
|
* [Job Control in Bash](#job-control-in-bash)
|
|
* [Vim editing for Bash](#vim-editing-for-bash)
|
|
* [Stop yourself from deleting files](#stop-yourself-from-deleting-files)
|
|
* [Reusing commands](#reusing-commands)
|
|
* [Automated interactive input](#automated-interactive-input)
|
|
* [Bash scripting](#bash-scripting)
|
|
* [Other Bash uses](#other-bash-uses)
|
|
2. [Ed for terminal file editing](#ed-for-terminal-file-editing)
|
|
3. [Awk the programming language](#awk-the-programming-language)
|
|
- [Built in variables](#built-in-variables)
|
|
- [Regular expression](#regular-expression)
|
|
- [Recipes](#recipes)
|
|
- [Advanced examples](#advanced-examples)
|
|
|
|
# Bash
|
|
###### Process priority
|
|
Since the processor must decide priority, it uses a scale from -20 through 20 to
|
|
rank what has priority, with -20 being the highest priority. By default,
|
|
processes launched by the user start with a niceness of 0
|
|
|
|
# nice --10 ./start_qemu.sh
|
|
# nice -n -10 ./start_qemu.sh
|
|
Starts the process, in this case `start_qemu.sh`, with a given niceness level,
|
|
in this case `-10`. The `-n` option increments from the base value, usually 0
|
|
|
|
# renice -2 1244
|
|
# renice -n -2 1244
|
|
Changes the niceness of a running process via process id, in this case 1244.
|
|
`-n` increments niceness from the current value, in this case by `-2`
|
|
|
|
$ ps -fl -C 1244
|
|
Check the niceness of process with id 1244
|
|
|
|
###### Job Control in Bash
|
|
You should not use jobs if you can help it! Especially when it comes to
|
|
input/output jobs are terribly unclear. Only use them for tasks that will never
|
|
ask for input or give an output. Use a multiplexer (`tmux`) instead
|
|
|
|
$ python -m SimpleHTTPServer 3000 &
|
|
Starts a process, in this case the python server, in the background. Just append
|
|
a `&` at the end
|
|
|
|
*Oh no, I didn't launch the process in the background*
|
|
^z
|
|
If you want to move suspend the current process, `^z` will suspend and stop it
|
|
|
|
$ bg %2
|
|
$ bg 2
|
|
$ %2 &
|
|
Continues the stopped job in the background. In this case, we use job identifier
|
|
1, which will start that specific job. You can omit the `%` with `bg`
|
|
|
|
$ fg %2
|
|
Bring a job back to the foreground. Uses identifiers much like `bg`
|
|
|
|
$ jobs -l
|
|
List all background jobs running. `-l` shows their process id
|
|
|
|
$ kill %2
|
|
Kills a background job, via identifiers. The `%` is not optional here!
|
|
|
|
$ wait %2
|
|
Waits for job identifier `2` to finish. Passing no arguments waits for all
|
|
background jobs
|
|
|
|
$ wlsunset -t 4000 -T 65000 -g 0.9 &
|
|
$ disown %1
|
|
Launches `wlsunset` in the background. Then, assuming it's job 1 under `jobs`,
|
|
disowns the process. The shell can now close with the process still running
|
|
|
|
$ wlsunset -t 4000 -T 65000 -g 0.9 & disown
|
|
Same as above. More compact
|
|
|
|
$ { wlsunset -t 4000 -T 65000 -g 0.9 & } &
|
|
$ ( wlsunset -t 4000 -T 65000 -g 0.9 & ) &
|
|
Same as above, background and disowns the subshell. `{}` execute the subshell in
|
|
the current shell's context, while `()` start a fresh shell. `{}` are also used
|
|
in brace expansion, so they need to be delimited by spaces
|
|
|
|
|
|
###### Vim editing for Bash
|
|
$ set -o vi
|
|
Makes bash use vi key binds. Note, this uses `vi` not `vim` bindings. Generally
|
|
this isn't recommended, even for complete vim users
|
|
|
|
$ export EDITOR='vim'
|
|
Bash will run the specified command when looking for an editor
|
|
|
|
<C-x><C-r>
|
|
Open current shell line in editor. Really powerful for long lines
|
|
|
|
See the (Ex section)[#Batch-editing-with-ex] for batch editing with vim
|
|
in `ex` mode
|
|
|
|
###### Stop yourself from deleting files
|
|
$ chmod a-w safe_directory
|
|
Prevents writing to the directory. Writing, in the sense of editing files is
|
|
still controlled by file permissions, you just can't remove or make new ones
|
|
|
|
# chown other_user safe_directory; chmod 755 safe_directory
|
|
Makes another user own the directory. All others uses cannot make/remove files.
|
|
Same as above, if ownership is given to root, though a bit more explicit
|
|
|
|
# touch safe_directory/file
|
|
# rm safe_directory/file
|
|
You can still create and delete files in this directory with root
|
|
|
|
###### Reusing commands
|
|
See HISTORY EXPANSION in `man bash` for more information
|
|
|
|
$ sudo !!
|
|
$ sudo !-1
|
|
Recalls the last command and runs it as root. Notice the `-1` can be any number
|
|
to recall older commands
|
|
|
|
$ !man
|
|
Rerun the last command starting with the string `man`
|
|
|
|
$ !?diff?:p
|
|
Print the last command that contained the string `diff`
|
|
|
|
$ mv !:2 ../!:2
|
|
Substitutes `!:2` with the second argument of the previous command. The command
|
|
itself is `!:0`, so it's the second argument. Here it moves a directory up
|
|
|
|
$ mv !^ !^'_name'
|
|
$ mv !$ !$'_name'
|
|
`$` and `^` are aliases for the first and last arguments. Here it changes a file
|
|
or directory name by adding `_name` at the end
|
|
|
|
$ vim -p !touch:*
|
|
$ vim -p !touch:2-4
|
|
$ vim -p !:2*
|
|
Expands argument ranges from the last `touch` command. `*`, `1-*` and `1*` are
|
|
synonymous. If no string is specified, like `!:*`, the last command is used
|
|
|
|
$ vim -o !touch:1*
|
|
Open a new window for every argument in the most recent command starting with
|
|
`touch`
|
|
|
|
$ ^touch^vim -o^
|
|
$ !!:s/touch/vim -o
|
|
$ !!:&
|
|
Replay the last command replacing the first instance of `touch` with `vim -o`.
|
|
The third line replaces `&` with the last substitution
|
|
|
|
$ vim -o !touch:*:gs/html/md
|
|
Recall all the arguments of the last `touch` command. Replace all instances of
|
|
`html` with `md` then pass those to `vim -o`
|
|
|
|
$ cat !!:$:s/machine/human
|
|
Substitute in the last argument from the previous command with `human`
|
|
|
|
<C-r>
|
|
<C-s>
|
|
Search backward or forward for input string. Use multiple times to scroll
|
|
|
|
awk '{a[NR] = $0} END { for (i = NR; i > 0; i--) print a[i] }' # rev lines
|
|
<C-r># rev lines
|
|
Replays the tagged command. Use trailing comments to act as a tag
|
|
|
|
diff ~/Downloads/{before,after}.txt
|
|
Checks if `before.txt` and `after.txt` are different. Braces are duplicated
|
|
|
|
$ !if:gs/$c/$a
|
|
Replace all the variables $c with $a in the previous command starting with if
|
|
|
|
So the general structure of recall is
|
|
```
|
|
!<cmd-identifier>[:<arg-range>][:<operator>]
|
|
```
|
|
|
|
###### Automated interactive input
|
|
$ yes | rm -ir /deep_dir
|
|
Bypasses interactive y/N prompt given by the `rm` command for every thing in that
|
|
directory. `yes` prints an infinite number of 'y\n' to any program
|
|
|
|
$ printf 'y\nn\ny\n' | rm -ir /small_dir
|
|
Will remove the first and third file automatically, then ask you for further
|
|
input
|
|
|
|
$ rm -ir /deep_dir < pre_made_input.txt
|
|
Enters the file contexts to the interactive prompt. Line breaks are like '\n'.
|
|
Very helpful if you're using this order of input over and over
|
|
|
|
###### Bash scripting
|
|
Bash is an awful scripting language in every sense except portability. Always
|
|
install and use `shellcheck` after writing a script
|
|
|
|
bash_script.sh
|
|
#!/bin/bash
|
|
#!/usr/bin/env bash
|
|
#!/usr/bin/env -S awk -f ${SOMEFILE}
|
|
Use shebang at the top to declare an interpreter. Using `env` is considered more
|
|
portable, though the `-S` option is required for anything longer than one word
|
|
|
|
$ echo ${myvar:-not here}
|
|
$ echo ${myvar:-"not here"}
|
|
Expands to `$myvar` if it's set, otherwise expands to string "not here"
|
|
|
|
$ for i in $(seq 0 9); do python -c "print(ord('$1'))" & done
|
|
Asynchronously print the ASCII codes for range `[0, 9]`
|
|
|
|
###### Other Bash uses
|
|
|
|
$ compgen -c git
|
|
See the possible completion for the given word. This is what tab uses
|
|
|
|
$ time bash -c "make && ./a.out; rm ./a.out"
|
|
Times multiple separate bash commands
|
|
|
|
## Ed for terminal file editing
|
|
Ed is the original text editor for Unix systems, which still is useful for batch
|
|
editing scripts. Ranges are very similar to vim's
|
|
|
|
$ ed -p '> :' file1
|
|
Open `file1` with ed. Useful for writing a script
|
|
|
|
:n Print the current line enumerated
|
|
:100 Moves to line 100 and prints it
|
|
:ka Set a mark `a`
|
|
:'a Move to mark `a`
|
|
:i Open line above current line in insert mode. Like vim's `o`
|
|
:a Open line below current line in insert mode. Like vim's `O`
|
|
. Exit insert mode. Should be the only character on the line
|
|
Various basic commands. Most commands accept ranges, like vim's. Unlike vim, all
|
|
commands operate on entire lines at once
|
|
|
|
:g/Style/p\
|
|
a\
|
|
Style line is above ^^^^\
|
|
.\
|
|
n
|
|
For all lines matching the regex, run the command sequence. `\` separate lines
|
|
|
|
See vim_batch_editing.md for practical application of this
|
|
|
|
## Awk the programming language
|
|
AWK is an old, though surprisingly useable stream-editing language. It's a POSIX
|
|
compliant tool to bundle `grep`, `sed`, `printf`, `cut`, `xargs`, and more all
|
|
into one scripting language. Most its use comes in bash scripts or one-liners to
|
|
quickly extract information from a text stream. The man pages are really good!
|
|
|
|
Much of the standard control flow is the same as C. Variables don't need to be
|
|
initialized before use, ex: `i+=1`. Many functions will perform on `$0` if no
|
|
argument is given
|
|
|
|
#!/usr/bin/env -S awk -f
|
|
#!/usr/bin/env -S awk -F: -f
|
|
Use the string option on `env` to be able to specify the necessary `-f` option
|
|
|
|
|
|
substr(str, start_pos, chars_cut) // Cut out part of a string
|
|
length([str]) // Length of str, or $0 if no argument is given
|
|
index(str, search_str) // Find starting index of match in str
|
|
match(str, /reg/) // Return index. Set RSTART and RLENGTH
|
|
split(str, array, fs) // Split str into array elements on FieldSeperator
|
|
sub(/reg/, new_str, str) // Subs new_str for first /reg/ match in str
|
|
sub("match", new_str, str) // Subs new_str for first string "match" in str
|
|
gsub(/reg/, new_str, str) // Above just global
|
|
The core awk functions with odd argument order
|
|
|
|
printf, sprintf, system, tolower, toupper, exp, log...
|
|
Core awk functions without odd argument order
|
|
|
|
### Built in variables
|
|
NR Current line's number. Literally "number of reads". Starts at 1
|
|
NF Number of fields in this line. `$NR` expands to the last field's value
|
|
RSTART Field number of last `match()`
|
|
RLENGTH How many characters long the last `match()` was
|
|
$0 The entire line of text
|
|
$1 Where `1` is the `1th` field in the line
|
|
|
|
### Regular expression
|
|
Regular expressions are supported. They're a separate type from strings. Unlike
|
|
most modern regex interpreters, escape sequences such as `\s` and `\d` aren't
|
|
supported. Instead use POSIX compliant versions `[:space:]` and `[:digit:]`
|
|
|
|
Inverting a regex is done by defining a `!` in front. For example
|
|
$0 !~ /hello/
|
|
$0 ~ !/hello/
|
|
$0 ~ /[^(hello)]/
|
|
|
|
If you want to store a regex in a variable for later use, DO NOT store an actual
|
|
regex type. Store the regex as a string instead, effectively replacing the
|
|
encapsulating `/` with `"`
|
|
|
|
my_regex = "hello[:space:]+you"
|
|
$0 ~ my_regex
|
|
Notably, using a regex instead of a string in this situation results in odd
|
|
silent errors that mostly work. These can be hard to debug, so watch out
|
|
|
|
### Recipes
|
|
$ cat file.txt | awk 'tolower($0) ~ /word/ { print $0 }'
|
|
$ cat file.txt | awk 'tolower($0) ~ /word/'
|
|
$ cat file.txt | grep -i 'word'
|
|
Searches through given piped text for 'word' in any casing
|
|
|
|
$ cat file.txt | awk -F ',' '
|
|
{ commas += NF - 1 }
|
|
END { print "Commas counted:", commas }'
|
|
Counts the number of commas in a file.
|
|
|
|
$ awk -F '\t' '{ print $0 }' demo.txt
|
|
$ awk -F '\t' demo.txt
|
|
Uses tabs are a files separator between "lines"
|
|
|
|
$ current_window_id=$(chrome-cli list windows | awk -F ' ' \
|
|
'NR == 1 { gsub(/[\[\]]/, "", $1); print $1 }')
|
|
Parses the window id from `[999] Some Title` to `999`
|
|
|
|
$ ls -la | awk 'match($NF, /[0-9]+/) { print substr($NF, RSTART, RLENGTH) }' >
|
|
coffees.nh
|
|
Checks if the last argument is a string of consecutive numbers. Notice how `$NF`
|
|
uses that `NF` is the last number to index the last field like `$3`
|
|
|
|
### Advanced examples
|
|
END {
|
|
arr["hello"] = 2; arr["other"]++; arr["some"] += 10;
|
|
|
|
printf "Hello: %d, Other: %d, Some: %d, None: %d\n",
|
|
arr["hello"], arr["other"], arr["some"], arr["none"];
|
|
}
|
|
>>> Output: Hello: 2, Other: 1, Some: 10, None: 0
|
|
Arrays can be indexed by strings! This allows for counting words or lines
|
|
|
|
$ awk '!($0 in uniq) { uniq[$0]; print }' duplicates.txt > uniques.txt
|
|
Removes duplicate lines. This uses the relational operator `(expr, expr... arr)`
|
|
to check if an element with the string's index has been initialized in the array
|
|
|
|
$ awk '!unique[$0]++' duplicates.txt > uniques.txt
|
|
Removes duplicate lines. This exploits awk's automatic initialization of
|
|
variables with falsey values, 0 here, then turns that index truthy with `++`
|