forked from andonome/lk
change formatting
input examples are now given as ```bash input $ARG1 ``` While outputs use md's '> ' sign as a quote.
This commit is contained in:
@@ -1,26 +1,79 @@
|
||||
---
|
||||
title: "archives"
|
||||
tags: [ "Documentation", "backups" ]
|
||||
title: "Archives"
|
||||
tags: [ "Documentation", "tar", "backups" ]
|
||||
---
|
||||
# GPG Archives
|
||||
# `tar`
|
||||
|
||||
Create an encrypted archive with `gpg`:
|
||||
## Create
|
||||
|
||||
> tar czvpf - file1.txt file2.pdf file3.jpg | gpg --symmetric --cipher-algo aes256 -o myarchive.tar.gz.gpg
|
||||
Combine many files and directories into a single t-archive file.
|
||||
|
||||
And extract it with `gpg`:
|
||||
```bash
|
||||
tar cf "$ARCHIVE".tar $DIR
|
||||
```
|
||||
You can remember this with the mnemonic '*C*reate *F*ile'.
|
||||
|
||||
> gpg -d myarchive.tar.gz.gpg | tar xzvf -
|
||||
Unfortunately, this stores the full file path, so making a tar archive of `/etc/nginx/` will store `etc/nginx` (without the leading `/`.
|
||||
|
||||
It's often better to tell tar which path to start from using the `-C` flag.
|
||||
|
||||
```bash
|
||||
tar cf "$ARCHIVE".tar -C /etc/ nginx
|
||||
```
|
||||
|
||||
Check the contents of your archive with:
|
||||
|
||||
```bash
|
||||
tar tf "$ARCHIVE".tar
|
||||
```
|
||||
|
||||
If you want to store 'everything in a directory', then using `*` will not work, because it will target everything in the *current* directory.
|
||||
|
||||
Instead, you can store the target in a variable:
|
||||
|
||||
```bash
|
||||
files=$(ls /etc/nginx)
|
||||
tar cf "$ARCHIVE".tar -C /etc/nginx/ $file
|
||||
```
|
||||
|
||||
## Extract
|
||||
|
||||
Extract the tar archive with
|
||||
|
||||
> tar xf "$ARCHIVE".tar
|
||||
|
||||
You can remember this with the mnemonic 'e*X*tract *F*ile'.
|
||||
|
||||
## Compress
|
||||
|
||||
Create a zip-compressed archive with the `z` flag.
|
||||
|
||||
```bash
|
||||
tar czf "$ARCHIVE".tgz -C /etc/nginx/ $file
|
||||
```
|
||||
|
||||
You can use any file ending you want, but sane people like to use '.tgz' or '.tar.tgz'.
|
||||
|
||||
# 7zip
|
||||
|
||||
(also called 'p7zip' or '7z')
|
||||
|
||||
Make archive:
|
||||
|
||||
> 7za a -tzip -pPASSWORD -mem=AES256 archive.zip file1 file2
|
||||
|
||||
```bash
|
||||
PASSWORD=my_password
|
||||
```
|
||||
```bash
|
||||
7za a -tzip -p$PASSWORD -mem=AES256 $ARCHIVE.zip $FILE_1 $FILE_2
|
||||
```
|
||||
Note that people can still see every filename in your archive, and can change those files.
|
||||
They just can't read the contents.
|
||||
|
||||
Unzip:
|
||||
|
||||
> 7za e archive.zip
|
||||
```bash
|
||||
7za x archive.zip
|
||||
```
|
||||
|
||||
7zip will open anything: zip-files, rar-files, a tin of beans, *anything*.
|
||||
However, the extracted tgz files will just be tar files, so you will still need to use tar to extract them (see above).
|
||||
|
@@ -5,28 +5,36 @@ tags: [ "Documentation", "Backups" ]
|
||||
|
||||
Install unison on both machines, and make sure both have the same version of unison, with the same version of the ocaml compiler (the smallest difference will cause problems).
|
||||
|
||||
> unison -version
|
||||
```bash
|
||||
unison -version
|
||||
```
|
||||
|
||||
Create the `~/.unison` directory on both machines.
|
||||
|
||||
Make a job called `backup`:
|
||||
|
||||
> vim ~/.unison/*backup*.prf
|
||||
|
||||
You can name the file anything, but it must end in .prf.
|
||||
|
||||
Here is an example job, which synchronizes the `~/music` directory with a remote machine.
|
||||
|
||||
```bash
|
||||
JOB=backup
|
||||
```
|
||||
|
||||
Here is an example job, which synchronizes the `~/music` directory with a remote machine which has the same username.
|
||||
|
||||
|
||||
```bash
|
||||
echo "
|
||||
auto = true
|
||||
root=/home/ghost
|
||||
root=ssh://ghost@192.168.0.10//home/ghost/
|
||||
root=$HOME
|
||||
root=ssh://$USER@$IP_ADDRESS/$HOME
|
||||
|
||||
path=music
|
||||
|
||||
ignore=Name *.flac
|
||||
" > ~/.unison/"$JOB".prf
|
||||
|
||||
```
|
||||
|
||||
Remember to specify `$IP_ADDRESS`
|
||||
|
||||
The last command means it will ignore any file with a name ending in `.flac`.
|
||||
|
||||
## Automatic Runs
|
||||
@@ -34,11 +42,12 @@ The last command means it will ignore any file with a name ending in `.flac`.
|
||||
The first command means this will run but also confirm which files will be deleted, and which will be transferred, us `batch = true` instead.
|
||||
Or you can deleted that line in the `.prf` file and run it with a flag:
|
||||
|
||||
> unison -batch *backup*.prf
|
||||
```bash
|
||||
unison -batch *backup*.prf
|
||||
```
|
||||
|
||||
Set unison to run with crontab or a systemd unit file to have directories synchronize automatically.
|
||||
|
||||
|
||||
## Problem Solving
|
||||
|
||||
You will see data files summarizing what has happened in the `~/.unison` directory.
|
||||
|
@@ -5,14 +5,20 @@ tags: [ "Documentation", "data" ]
|
||||
|
||||
Install, and add with
|
||||
|
||||
> git lfs install
|
||||
```bash
|
||||
git lfs install
|
||||
```
|
||||
|
||||
Then track some filetype with:
|
||||
|
||||
> git lfs track "\*.ttf"
|
||||
```bash
|
||||
git lfs track "\*.ttf"
|
||||
```
|
||||
|
||||
Or a directory with:
|
||||
|
||||
> git lfs track "images/"
|
||||
```bash
|
||||
git lfs track "images/"
|
||||
```
|
||||
|
||||
All changes require adding `.gitattributes`.
|
||||
|
146
data/git.md
146
data/git.md
@@ -6,93 +6,140 @@ tags: [ "Documentation", "data" ]
|
||||
|
||||
## New Machines
|
||||
|
||||
> git config --global user.email *"malinfreeborn@posteo.net"*
|
||||
```bash
|
||||
git config --global user.email "$YOUR_EMAIL"
|
||||
```
|
||||
|
||||
> git config --global user.name *"Malin Freeborn"*
|
||||
```bash
|
||||
git config --global user.name "$YOUR_NAME"
|
||||
```
|
||||
|
||||
# New Git
|
||||
|
||||
Start a git in a folder:
|
||||
Start a git in directory `$DIR`:
|
||||
|
||||
> mkdir *project* && cd *project*
|
||||
```bash
|
||||
mkdir $DIR && cd $DIR
|
||||
```
|
||||
|
||||
> git init
|
||||
```bash
|
||||
git init
|
||||
```
|
||||
|
||||
Make a file explaining what the project does:
|
||||
|
||||
> vim README.md
|
||||
```bash
|
||||
vim README.md
|
||||
```
|
||||
|
||||
> git add README.md
|
||||
Add this to the git:
|
||||
|
||||
```bash
|
||||
git add README.md
|
||||
```
|
||||
|
||||
Then make the initial commit, explaining the change you just made:
|
||||
|
||||
> git commit
|
||||
```bash
|
||||
git commit
|
||||
```
|
||||
|
||||
# Working
|
||||
|
||||
Once you make a change to some file ("file.sh"), add it and make a commit explaining it.
|
||||
Once you make a change to some file, add it and make a commit explaining it.
|
||||
|
||||
> git add file.sh
|
||||
```bash
|
||||
git add $FILE
|
||||
```
|
||||
|
||||
> git commit -m"change file.sh"
|
||||
```bash
|
||||
git commit -m"change $FILE"
|
||||
```
|
||||
|
||||
Check your history:
|
||||
|
||||
> git log
|
||||
```bash
|
||||
git log
|
||||
```
|
||||
|
||||
# Remotes
|
||||
|
||||
If you want to keep a copy on a public site such as Gitlab, so others can see it, then go there and create a blank project (no readme, nothing).
|
||||
Find the address you want and add it as a remote:
|
||||
Give it the same name as the `$DIR` directory, above.
|
||||
|
||||
> git remote add *gitlab* *https://gitlab.com/username/projectx*
|
||||
Add this as a remote:
|
||||
|
||||
```bash
|
||||
REMOTE=gitlab
|
||||
git remote add $REMOTE https://gitlab.com/$USERNAME/$DIR
|
||||
```
|
||||
|
||||
Tell git you're pushing the branch "master" to the remote repo "origin":
|
||||
|
||||
> git push -u master origin
|
||||
```bash
|
||||
git push -u master origin
|
||||
```
|
||||
|
||||
If someone makes a change on the remote, pull it down with:
|
||||
|
||||
> git pull
|
||||
```bash
|
||||
git pull
|
||||
```
|
||||
|
||||
# Branches
|
||||
|
||||
A branch is a full copy of the project to test additional ideas.
|
||||
You can make a new branch called 'featurez' like this:
|
||||
|
||||
> git branch *featurez*
|
||||
```bash
|
||||
git branch *featurez*
|
||||
```
|
||||
|
||||
Have a look at all your branches:
|
||||
|
||||
> git branch
|
||||
```bash
|
||||
git branch
|
||||
```
|
||||
|
||||
Switch to your new branch:
|
||||
|
||||
> git checkout *featurez*
|
||||
```bash
|
||||
git checkout *featurez*
|
||||
```
|
||||
|
||||
And if your changes are rubbish, checkout the "master" branch again, then delete "featurez":
|
||||
|
||||
> git branch -D *featurez*
|
||||
```bash
|
||||
git branch -D *featurez*
|
||||
```
|
||||
|
||||
Or if it's a good branch, push it to the remote:
|
||||
|
||||
> git push *origin* *featurez*
|
||||
```bash
|
||||
git push *origin* *featurez*
|
||||
```
|
||||
|
||||
## Merging
|
||||
|
||||
Once you like the feature, merge it into the main branch. Switch to master then merge it:
|
||||
|
||||
> git merge *featurez*
|
||||
```bash
|
||||
git merge *featurez*
|
||||
```
|
||||
|
||||
and delete `featurez` as you've already merged it:
|
||||
|
||||
> git branch -d featurez
|
||||
```bash
|
||||
git branch -d featurez
|
||||
```
|
||||
|
||||
# Subtree
|
||||
|
||||
## Pulling another git repo into a subtree
|
||||
|
||||
> git subtree add -P config git@gitlab.com:bindrpg/config.git master
|
||||
```bash
|
||||
git subtree add -P config git@gitlab.com:bindrpg/config.git master
|
||||
```
|
||||
|
||||
## Pulling a Subtree from an existing git
|
||||
|
||||
@@ -100,13 +147,17 @@ The project has subdirectories sub-1,sub-2,sub-3. The first should be its own r
|
||||
|
||||
First, we extract its history as an independent item, and make that into a seprate branch.
|
||||
|
||||
> git subtree split --prefix=sub-1 -b sub
|
||||
```bash
|
||||
git subtree split --prefix=sub-1 -b sub
|
||||
```
|
||||
|
||||
If you want something a few directories deep, you can use `--prefix=sub-1/dir-2/dir-3
|
||||
|
||||
Then go and create a new git somewhere else:
|
||||
|
||||
> cd ..;mkdir sub-1;cd sub-1;git init --bare
|
||||
```bash
|
||||
cd ..;mkdir sub-1;cd sub-1;git init --bare
|
||||
```
|
||||
|
||||
Then go back to your initial git repo, and do the following:
|
||||
|
||||
@@ -114,38 +165,57 @@ git push ../subtest sub:master
|
||||
|
||||
Finally, you can clone this repo from your original.
|
||||
|
||||
> git clone ../subtest
|
||||
```bash
|
||||
git clone ../subtest
|
||||
```
|
||||
|
||||
# Tricks
|
||||
|
||||
## Delete All History
|
||||
|
||||
> git checkout --orphan temp
|
||||
```bash
|
||||
git checkout --orphan temp
|
||||
```
|
||||
|
||||
> git add -A
|
||||
```bash
|
||||
git add -A
|
||||
```
|
||||
|
||||
> git commit -am "release the commits!"
|
||||
```bash
|
||||
git commit -am "release the commits!"
|
||||
```
|
||||
|
||||
> git branch -D master
|
||||
```bash
|
||||
git branch -D master
|
||||
```
|
||||
|
||||
> git branch -m master
|
||||
```bash
|
||||
git branch -m master
|
||||
```
|
||||
|
||||
> git push -f origin master
|
||||
```bash
|
||||
git push -f origin master
|
||||
```
|
||||
|
||||
Gitlab requires more changes, such as going to `settings > repository` and switching the main branch, then stripping protection.
|
||||
|
||||
## Clean up Bloated Repo
|
||||
|
||||
> git fsck --full
|
||||
```bash
|
||||
git fsck --full
|
||||
```
|
||||
|
||||
> git gc --prune=now --aggressive
|
||||
```bash
|
||||
git gc --prune=now --aggressive
|
||||
```
|
||||
|
||||
> git repack
|
||||
```bash
|
||||
git repack
|
||||
```
|
||||
|
||||
## Find Binary Blobs
|
||||
|
||||
```
|
||||
|
||||
```bash
|
||||
git rev-list --objects --all \
|
||||
| git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \
|
||||
| sed -n 's/^blob //p' \
|
||||
|
@@ -1,54 +0,0 @@
|
||||
---
|
||||
title: "gpg keys with ssh"
|
||||
tags: [ "Documentation", "distros" ]
|
||||
---
|
||||
|
||||
<!--
|
||||
Source:
|
||||
https://ryanlue.com/posts/2017-06-29-gpg-for-ssh-auth
|
||||
-->
|
||||
|
||||
Install `gnupg`.
|
||||
|
||||
Generate a new gpg key just for authentication:
|
||||
|
||||
> gpg2 --expert --edit-key 024C6B1C84449BD1CB4DF7A152295D2377F4D70F
|
||||
|
||||
Toggle options `S`, `E`, and `A` until the following output:
|
||||
|
||||
```
|
||||
Current allowed actions: Authenticate
|
||||
```
|
||||
|
||||
Add ssh to the gpg key agent.
|
||||
|
||||
> echo enable-ssh-support >> ~/.gnupg/gpg-agent.conf
|
||||
|
||||
This won't take effect until you restart the gpg agent, so kill it:
|
||||
|
||||
> gpgconf --kill gpg-agent
|
||||
|
||||
> gpgconf --launch gpg-agent
|
||||
|
||||
Use 2048 (or whatever) bits, save, and exit.
|
||||
|
||||
Add this to your `~/.bash_profile`:
|
||||
|
||||
```
|
||||
export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
|
||||
```
|
||||
|
||||
> source ~/.bash_profile
|
||||
|
||||
Find the ssh key's keygrip with:
|
||||
|
||||
> gpg -k --with-keygrip
|
||||
|
||||
It's the one with `[A]` next to it.
|
||||
Add it to `~/.gnupg/sshcontrol`.
|
||||
|
||||
> echo 1P0P6SA7S07Q8198414P126OR0514R3R8Q1389SP > ~/.gnupg/sshcontrol
|
||||
|
||||
Confirm it's added:
|
||||
|
||||
> ssh-add -l
|
52
data/gpg.md
52
data/gpg.md
@@ -6,13 +6,17 @@ tags: [ "Documentation", "data" ]
|
||||
|
||||
Generate keys:
|
||||
|
||||
> gpg --gen-key
|
||||
```bash
|
||||
gpg --gen-key
|
||||
```
|
||||
|
||||
Follow the guide.
|
||||
|
||||
# Encrypting a file
|
||||
|
||||
> gpg -r malinfreeborn@posteo.net -e file
|
||||
```bash
|
||||
gpg -r malinfreeborn@posteo.net -e file
|
||||
```
|
||||
|
||||
`-r` specifies the recipient.
|
||||
|
||||
@@ -28,11 +32,15 @@ gpg --list-keys
|
||||
|
||||
Make a password with a password (cypher encryption).
|
||||
|
||||
> gpg -c --output passwords.txt
|
||||
```bash
|
||||
gpg -c --output passwords.txt
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
> gpg -c > passwords.txt
|
||||
```bash
|
||||
gpg -c > passwords.txt
|
||||
```
|
||||
|
||||
Put in a password.
|
||||
|
||||
@@ -40,17 +48,23 @@ Write message then stop with Ctrl+d.
|
||||
|
||||
Get the message back out the file with:
|
||||
|
||||
> gpg -d passwords.txt
|
||||
```bash
|
||||
gpg -d passwords.txt
|
||||
```
|
||||
|
||||
# Circles of Trust
|
||||
|
||||
Search for a key at any key store:
|
||||
|
||||
> gpg --search-keys nestorv
|
||||
```bash
|
||||
gpg --search-keys nestorv
|
||||
```
|
||||
|
||||
Once you've made a decision about someone:
|
||||
|
||||
> gpg --list-keys
|
||||
```bash
|
||||
gpg --list-keys
|
||||
```
|
||||
|
||||
You get something like this:
|
||||
|
||||
@@ -67,27 +81,39 @@ This is a fingerprint.
|
||||
|
||||
You can now decide the trust level (this stays on your computer).
|
||||
|
||||
> gpg --edit-key *CD30421FD825696BD95F1FF644C62C57B790D3CF*
|
||||
```bash
|
||||
gpg --edit-key *CD30421FD825696BD95F1FF644C62C57B790D3CF*
|
||||
```
|
||||
|
||||
Once you're in the interface, type `trust`.
|
||||
|
||||
> gpg --sign-key alice@posteo.net
|
||||
```bash
|
||||
gpg --sign-key alice@posteo.net
|
||||
```
|
||||
|
||||
Then send those trusted keys up to a server, so people can see you have verified them:
|
||||
|
||||
> gpg --send-keys *024C6B1C84449BD1CB4DF7A152295D2377F4D70F*
|
||||
```bash
|
||||
gpg --send-keys *024C6B1C84449BD1CB4DF7A152295D2377F4D70F*
|
||||
```
|
||||
|
||||
# Refresh Keys
|
||||
|
||||
> gpg --refresh-keys
|
||||
```bash
|
||||
gpg --refresh-keys
|
||||
```
|
||||
|
||||
# Export
|
||||
|
||||
Your public key:
|
||||
|
||||
> gpg --output *me*.gpg --armor --export
|
||||
```bash
|
||||
gpg --output *me*.gpg --armor --export
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
> gpg --export -a *person@email.tld* > *my_key*.pub
|
||||
```bash
|
||||
gpg --export -a *person@email.tld* > *my_key*.pub
|
||||
```
|
||||
|
||||
|
@@ -4,37 +4,57 @@ tags: [ "Documentation", "Data" ]
|
||||
---
|
||||
Get the basic config:
|
||||
|
||||
> mkdir ~/.config/khard
|
||||
```bash
|
||||
mkdir ~/.config/khard
|
||||
```
|
||||
|
||||
> cp /usr/share/doc/khard/examples/khard/khard.conf.example ~/.config/khard.conf
|
||||
```bash
|
||||
cp /usr/share/doc/khard/examples/khard/khard.conf.example ~/.config/khard.conf
|
||||
```
|
||||
|
||||
Short list
|
||||
|
||||
> khard list
|
||||
```bash
|
||||
khard list
|
||||
```
|
||||
|
||||
Longer list
|
||||
|
||||
> khard show
|
||||
```bash
|
||||
khard show
|
||||
```
|
||||
|
||||
Show from addressbook 'work'
|
||||
|
||||
> khard list -a work
|
||||
```bash
|
||||
khard list -a work
|
||||
```
|
||||
|
||||
Make a new contact in address book 'family'
|
||||
|
||||
> khard new -a family
|
||||
```bash
|
||||
khard new -a family
|
||||
```
|
||||
|
||||
> khard edit grampa
|
||||
```bash
|
||||
khard edit grampa
|
||||
```
|
||||
|
||||
> khard remove bob
|
||||
```bash
|
||||
khard remove bob
|
||||
```
|
||||
|
||||
Move contact 'nina' from 'work' to 'home' address book.
|
||||
|
||||
> khard move -a home nina -A work
|
||||
```bash
|
||||
khard move -a home nina -A work
|
||||
```
|
||||
|
||||
## Advanced
|
||||
|
||||
Merge:
|
||||
|
||||
> khard merge [-a source_abook] [-u uid|search terms [search terms ...]] [-A target_abook] [-U target_uid|-t target_search_terms]
|
||||
```bash
|
||||
khard merge [-a source_abook] [-u uid|search terms [search terms ...]] [-A target_abook] [-U target_uid|-t target_search_terms]
|
||||
```
|
||||
|
||||
|
@@ -4,9 +4,13 @@ tags: [ "Documentation", "RSS" ]
|
||||
---
|
||||
Create the configuration directory before you start, and add at least 1 URL.
|
||||
|
||||
> mkdir ~/.config/newsboat
|
||||
```bash
|
||||
mkdir ~/.config/newsboat
|
||||
```
|
||||
|
||||
> echo 'https://voidlinux.org/atom.xml foss tech' >> ~/.config/newsboat/urls
|
||||
```bash
|
||||
echo 'https://voidlinux.org/atom.xml foss tech' >> ~/.config/newsboat/urls
|
||||
```
|
||||
|
||||
Start `newsobat` and press `r` to load your feed.
|
||||
|
||||
@@ -24,7 +28,9 @@ You can input a Youtube channel by adding this, with the channel's ID at the end
|
||||
|
||||
To get the channel ID without hunting:
|
||||
|
||||
> curl *'https://www.youtube.com/@1minfilms'* | grep -oE 'browseId":"U\w+"' | tail | cut -d'"' -f3
|
||||
```bash
|
||||
curl *'https://www.youtube.com/@1minfilms'* | grep -oE 'browseId":"U\w+"' | tail | cut -d'"' -f3
|
||||
```
|
||||
|
||||
You can add arbitrary commands to get an RSS feed.
|
||||
For example, to get a Gemini feed, install `gemget`, then put this in the configuration file:
|
||||
@@ -58,9 +64,11 @@ Or or `,o` to open an article in w3m:
|
||||
|
||||
Add vim-like keys:
|
||||
|
||||
```
|
||||
bind-key j next
|
||||
bind-key k prev
|
||||
bind-key j down article
|
||||
bind-key k up article
|
||||
```
|
||||
> bind-key j next
|
||||
|
||||
> bind-key k prev
|
||||
|
||||
> bind-key j down article
|
||||
|
||||
> bind-key k up article
|
||||
|
||||
|
26
data/pass.md
26
data/pass.md
@@ -8,21 +8,35 @@ Setup [gpg](./gpg.md) keys.
|
||||
|
||||
Show your gpg secret it:
|
||||
|
||||
> gpg --list-secret-keys
|
||||
```bash
|
||||
gpg --list-secret-keys
|
||||
```
|
||||
|
||||
Then use the id number under `sec` to make a pass repo:
|
||||
|
||||
> pass init 187233O300300814PQ793NSSS539SQ1O6O184532
|
||||
```bash
|
||||
KEY="$(gpg --list-secret-keys | grep -m 1 -A1 '^sec' | tail -n 1)"
|
||||
```
|
||||
|
||||
To add a basic password, e.g. for an encrypted tarball, use:
|
||||
```bash
|
||||
pass init $KEY
|
||||
```
|
||||
|
||||
> pass add my-tar-gar.gz
|
||||
To add a basic password, e.g. for `$WEBSITE`:
|
||||
|
||||
```bash
|
||||
pass $WEBSITE
|
||||
```
|
||||
|
||||
To insert a multiline password, e.g. with a login name:
|
||||
|
||||
> pass add -m linuxrocks.online
|
||||
```bash
|
||||
pass add -m $WEBSITE
|
||||
```
|
||||
|
||||
Remove a password:
|
||||
|
||||
> pass rm linuxrocks.online
|
||||
```bash
|
||||
pass rm $WEBSITE
|
||||
```
|
||||
|
||||
|
@@ -12,10 +12,13 @@ Arch: tesseract-data-eng and poppler-utils
|
||||
|
||||
## Script
|
||||
|
||||
> pdftoppm -png *file*.pdf test
|
||||
```bash
|
||||
pdftoppm -png *file*.pdf test
|
||||
```
|
||||
|
||||
> for x in \*png; do
|
||||
> tesseract -l eng "$x" - >> *out*.txt
|
||||
> done
|
||||
```bash
|
||||
for x in \*png; do
|
||||
tesseract -l eng "$x" - >> *out*.txt
|
||||
done
|
||||
```
|
||||
|
||||
- [Example script](data/pdf-to-txt.sh)
|
||||
|
1009
data/sql/person.sql
1009
data/sql/person.sql
File diff suppressed because it is too large
Load Diff
@@ -1,364 +0,0 @@
|
||||
---
|
||||
title: "postgresql"
|
||||
tags: [ "Documentation", "data" ]
|
||||
---
|
||||
# Setup
|
||||
|
||||
Install postgres and start it as a service, then start with:
|
||||
|
||||
> psql
|
||||
|
||||
## Arch setup
|
||||
|
||||
> su -l postgres
|
||||
|
||||
> initdb -D /var/lib/postgres/data
|
||||
|
||||
## Make a database as the new user postgres
|
||||
|
||||
> sudo su postgres
|
||||
|
||||
> [postgres] echo $HOME
|
||||
|
||||
> [postgres]
|
||||
|
||||
> [postgres] CREATE DATABASE dvdrental;
|
||||
|
||||
## Sample Data
|
||||
|
||||
Get sample data.
|
||||
|
||||
> wget http://www.postgresqltutorial.com/wp-content/uploads/2019/05/dvdrental.zip
|
||||
|
||||
And then get the pdf mapping the sample data:
|
||||
|
||||
> wget http://www.postgresqltutorial.com/wp-content/uploads/2018/03/printable-postgresql-sample-database-diagram.pdf
|
||||
|
||||
Unzip and load sample data:
|
||||
|
||||
> unzip dvdrental.zip
|
||||
|
||||
> sudo su postgres
|
||||
|
||||
|
||||
> [postgres] $ pg_restore -U postgres -d dvdrental dvdrental.tar
|
||||
|
||||
|
||||
> [postgres]
|
||||
|
||||
# Commands
|
||||
|
||||
## Basics
|
||||
|
||||
List available databases.
|
||||
|
||||
> \l
|
||||
|
||||
You'll see a list of available databases like:
|
||||
|
||||
`dnd`
|
||||
|
||||
`dvdrentals`
|
||||
|
||||
Then you can connect to one:
|
||||
|
||||
> \c dvdrental
|
||||
|
||||
And have a look at what tables it has:
|
||||
|
||||
> \d dvdrental
|
||||
|
||||
If it has tables such as `language`, `film_actor` and `inventory`, you can see the table's settings with:
|
||||
|
||||
> \dt film_actor
|
||||
|
||||
And pull back the entire table:
|
||||
|
||||
> SELECT * from film_actor;
|
||||
|
||||
## Various
|
||||
|
||||
Connect to 231.13.48.38 with user 'bob', port 1234, database 'X'
|
||||
|
||||
> psql -h 231.13.48.38 -p1234 -U bob X
|
||||
|
||||
# Setup Yourself
|
||||
|
||||
Make database "test" and connect to it.
|
||||
|
||||
> CREATE DATABASE test;
|
||||
|
||||
> \l test
|
||||
|
||||
Delete database 'dogs':
|
||||
|
||||
> DROP DATABASE dogs;
|
||||
|
||||
Making a table has a basic form of:
|
||||
|
||||
`CREATE TABLE table_name (`
|
||||
|
||||
then [ column name ] + [data type ] ... (and possibly data constraints)
|
||||
|
||||
`)`
|
||||
|
||||
|Data Types | Meaning | Constraints |
|
||||
|:----|:----|:----|
|
||||
| BIGSERIAL | A number incrementing by one each entry | 'NOT NULL PRIMARY KEY (so it's used for relational reference) |
|
||||
| int | integer | (50) limits the table to 50, e.g. `int(50)`|
|
||||
| VARCHAR | any characters | limit, e.g.`VARCHAR(70)`|
|
||||
| TIMESTAMP | time | |
|
||||
| date | date | |
|
||||
| text | text? | |
|
||||
| tsquery | text search query | |
|
||||
| money | money | |
|
||||
| json | textual JSON data | |
|
||||
| cidr | ipv4 or 6 address | |
|
||||
| macaddr | mac address | |
|
||||
|
||||
|
||||
E.g.
|
||||
|
||||
```
|
||||
CREATE TABLE character (
|
||||
id int,
|
||||
str int(1),
|
||||
dex int(1),
|
||||
spd int(1),
|
||||
int int(1),
|
||||
wts int(1),
|
||||
cha int(1));
|
||||
|
||||
```
|
||||
|
||||
See your table:
|
||||
|
||||
> \d
|
||||
|
||||
Look at what columns you have there:
|
||||
|
||||
> \d character
|
||||
|
||||
But this allows for empty characters, so...
|
||||
|
||||
```
|
||||
|
||||
CREATE TABLE person (
|
||||
id BIGSERIAL NOT NULL PRIMARY KEY,
|
||||
first_name VARCHAR(50) NOT NULL,
|
||||
last_name VARCHAR(50) NOT NULL,
|
||||
last_name VARCHAR(50) NOT NULL,
|
||||
gender VARCHAR(7) NOT NULL,
|
||||
date_of_birth DATE NOT NULL,
|
||||
);
|
||||
|
||||
```
|
||||
|
||||
Delete with
|
||||
|
||||
> DROP TABLE person;
|
||||
|
||||
## Inserting Data
|
||||
|
||||
```
|
||||
|
||||
INSERT INTO person (
|
||||
first_name,
|
||||
last_name,
|
||||
gender,
|
||||
date_of_birth)
|
||||
VALUES ('Hugi','Smith','DWARF', date '200-01-12');
|
||||
|
||||
```
|
||||
|
||||
## Selecting Data
|
||||
You can also mass select by choosing to insert a file. Download example data [here](https://mockaroo.com/).
|
||||
|
||||
> \i /home/ghost/file.sql
|
||||
|
||||
Various querries:
|
||||
|
||||
> SELECT * FROM person;
|
||||
|
||||
> SELECT * FROM person ORDER BY id DESC;
|
||||
|
||||
> SELECT * FROM person
|
||||
|
||||
## Offset, Fetch and Limit
|
||||
|
||||
'Limit' is not official, but was accepted later:
|
||||
|
||||
> SELECT * FROM person ORDER BY country ASC LIMIT 10;
|
||||
|
||||
The official way to make a limit is 'FIRST 5 ROWS ONLY:
|
||||
|
||||
> SELECT * FROM person OFFSET 5 FETCH FIRST 5 ROWS ONLY;
|
||||
|
||||
> SELECT * FROM person where gender = 'Male' AND ( country_of_birth = 'Poland' OR country_of_birth = 'China');
|
||||
|
||||
Miss out the first 5 result with 'OFFSET 5'.
|
||||
|
||||
> SELECT p\* FROM PERSON WHERE gender = 'Female' AND country_of_birth = 'Kosovo' OFFSET 5;
|
||||
|
||||
> SELECT * FROM person OFFSET 5 FETCH FIRST 7 ROW ONLY;
|
||||
|
||||
## Advanced Selection
|
||||
|
||||
This query takes a lot of typing:
|
||||
|
||||
> SELECT * FROM person WHERE country_of_birth = 'China'
|
||||
> OR country_of_birth = 'Kosovo'
|
||||
> OR country_of_birth = 'Brazil';
|
||||
|
||||
You can write the same thing with less typing:
|
||||
|
||||
> SELECT *
|
||||
> FROM person
|
||||
> WHERE country_of_birth in ('China','Kosovo','Brazil');
|
||||
|
||||
> SELECT * FROM person
|
||||
> WHERE date_of_birth
|
||||
BETWEEN DATE '2018-04-10' AND '2019-01-01'
|
||||
> ORDER BY date_of_birth;
|
||||
|
||||
### Rough Search
|
||||
|
||||
Similar words - we can find emails ending in '.com'.
|
||||
|
||||
> SELECT * FROM person
|
||||
> WHERE email LIKE '%.com';
|
||||
|
||||
Or any gmail address:
|
||||
|
||||
> SELECT * FROM person
|
||||
> WHERE email LIKE '%@gmail.%';
|
||||
|
||||
Or particular characters, where three precede 'gmail.com' and it's case insensitive:
|
||||
|
||||
> SELECT * FROM person
|
||||
> WHERE email iLIKE '\_\_\_@gmail.com';
|
||||
|
||||
### Groups and Aggregates
|
||||
|
||||
Select all countries as a complete mess:
|
||||
|
||||
> SELECT country_of_birth FROM person;
|
||||
|
||||
Select countries with proper grouping:
|
||||
|
||||
> SELECT country_of_birth FROM person GROUP BY country_of_birth;
|
||||
|
||||
Select countries and count instances:
|
||||
|
||||
> SELECT country_of_birth, COUNT(\*) FROM person GROUP BY country_of_birth ORDER BY country_of_birth;
|
||||
|
||||
Also select a minimum number with 'having'. What you have must be before 'order by'.
|
||||
|
||||
> SELECT country_of_birth, COUNT(\*) FROM person GROUP BY country_of_birth HAVING COUNT(\*) > 5;
|
||||
|
||||
> SELECT country_of_birth, COUNT(\*) FROM person GROUP BY country_of_birth HAVING COUNT(\*) >= 10;
|
||||
|
||||
Other aggregates include 'max', 'min'.
|
||||
|
||||
Select most expensive car:
|
||||
|
||||
> SELECT MAX(price) FROM car;
|
||||
|
||||
> SELECT MIN(price) FROM car;
|
||||
|
||||
> SELECT AVG(price) FROM car;
|
||||
|
||||
We can stick items together for better grouping:
|
||||
|
||||
> SELECT make, model, MAX(price) FROM car GROPU BY make, model;
|
||||
|
||||
Select all fields from table 'car', and add a column containing another price, discounted to 90%, rounded to two decimal places.
|
||||
|
||||
> SELECT id,make,model,price,ROUND(price * .9, 2) from car;
|
||||
|
||||
Same thing, but take 10% of the price from the price.
|
||||
|
||||
> SELECT id,make,model,price,ROUND(price - (price * .1), 2) from car;
|
||||
|
||||
|
||||
|
||||
## Comparison
|
||||
|
||||
> SELECT 10 + 2^2;
|
||||
|
||||
> SELECT 10! * 2 - 3;
|
||||
|
||||
... et c.
|
||||
|
||||
This returns false:
|
||||
|
||||
> SELECT 1 = 1;
|
||||
|
||||
These return false:
|
||||
|
||||
> SELECT 2<1;
|
||||
|
||||
Or '1 is not equal to 1':
|
||||
|
||||
> SELECT 1<>1;
|
||||
|
||||
And with strings, 'G is not the same as g':
|
||||
|
||||
> SELECT 'G'<>'g';
|
||||
|
||||
### Car Disconts
|
||||
|
||||
You want to show the discounts on various cars. You check which columns are available and select all of them:
|
||||
|
||||
> \d car
|
||||
|
||||
> SELECT id,make,model,price FROM car;
|
||||
|
||||
## Aliases
|
||||
|
||||
You can change what a column name appears as with:
|
||||
|
||||
> select price AS original_price from car;
|
||||
|
||||
# Null Values
|
||||
|
||||
## Coalesce
|
||||
|
||||
You can input a series of entries, requesting the first one which is present. Here we input three entries which are 'null', and a third which is '2', so '2' is selected:
|
||||
|
||||
> SELECT COALESCE(null, null, 2) AS number;
|
||||
|
||||
When selecting column 'email' from table 'person', you can input the string 'Email not provided' if there is no email provided:
|
||||
|
||||
> SELECT COALESCE(email, 'Email not provided') from person;
|
||||
|
||||
## Nullif
|
||||
|
||||
Normally, devision by 0 produces an error:
|
||||
|
||||
> SELECT 10/ 0;
|
||||
|
||||
But 10 divided by 'null' produces only 'null', which is not an error.
|
||||
|
||||
The 'nullif' statement takes two numbers, and returns 'null' iff the numbers are the same as each other.
|
||||
|
||||
> select nullif(0,0)
|
||||
> select nullif(10,10)
|
||||
|
||||
# Date
|
||||
|
||||
Select date:
|
||||
|
||||
> SELECT NOW()::DATE;
|
||||
|
||||
> SELECT NOW()::TIME;
|
||||
|
||||
or just:
|
||||
|
||||
> SELECT NOW();
|
||||
|
||||
More [here](postgresql.org/docs/11/datatype-datetime.html).
|
||||
|
||||
|
||||
2h23m
|
||||
|
@@ -1,94 +0,0 @@
|
||||
---
|
||||
title: "sql"
|
||||
tags: [ "Documentation", "data" ]
|
||||
---
|
||||
MySQL, Aurora and the Maria Database work similarly, and mostly with the same commands.
|
||||
|
||||
MySQL requires 160 Megs of disk space.
|
||||
|
||||
The ontological layers go:
|
||||
|
||||
> Database > table > record > field
|
||||
|
||||
The record is a line containing multiple fields. The table contains multiple records.
|
||||
|
||||
## Database: RPGs
|
||||
|
||||
### Table: D&D
|
||||
|
||||
#### Columns:
|
||||
|
||||
| id | name | year | edition | stars |
|
||||
|:--:|:-------------------|:-----|:--------|:------|
|
||||
| 1 | Dungeons & Dragons | 1975 | 1 | 1 |
|
||||
| 2 | Dungeons & Dragons | 1980 | 2 | 1 |
|
||||
| 3 | Advanced Dungeons & Dragons | 1985 | 1 | 1 |
|
||||
|
||||
|
||||
# Getting started
|
||||
|
||||
> sudo apt-get install mysql-server
|
||||
|
||||
You'll be asked for a password.
|
||||
|
||||
Log in with:
|
||||
|
||||
> mysql -u root -p
|
||||
|
||||
The -u requests a user, while -p tells it to prompt for a password.
|
||||
|
||||
List all databases:
|
||||
|
||||
> show databases;
|
||||
|
||||
Make a new database;
|
||||
|
||||
> create database creatures;
|
||||
|
||||
Start work on the new database:
|
||||
|
||||
> use creatures;
|
||||
|
||||
> create table stats (Strength VARCHAR(2), Speed VARCHAR(2), Dexterity(2));
|
||||
|
||||
This creatures a row called 'stats' within the 'creature'table' with a number of variables, all of type VARCHAR (a variable length character string).
|
||||
|
||||
Now you can insert data (which would normally be provided by a user via php or some-such).
|
||||
|
||||
> insert into stats (Strength,Speed,Dexterity) values (-1,0,+1)
|
||||
|
||||
Now have a look at the info:
|
||||
|
||||
> select * from stats
|
||||
|
||||
The old way to delete info by selection was:
|
||||
|
||||
> delete * from stats where Charisma='0'
|
||||
|
||||
...but now it's:
|
||||
|
||||
> delete from stats where Charisma='0'
|
||||
|
||||
Update a thing:
|
||||
|
||||
> update stats
|
||||
|
||||
> set Speed='-1',Charisma='-2'
|
||||
|
||||
> where Strength=0;
|
||||
|
||||
Leaving out the specifier 'where' means you're updating the entire database.
|
||||
|
||||
Control order with
|
||||
|
||||
> SELECT * FROM stats ORDER BY Strength;
|
||||
|
||||
Or for descending order, suffix 'DESC'.
|
||||
|
||||
> select * from stats ORDER by Date DESC;
|
||||
|
||||
# Resources
|
||||
|
||||
Try more at [w3schools](http://www.w3schools.com/sql/sql_groupby.asp).
|
||||
|
||||
|
@@ -5,95 +5,137 @@ tags: [ "Documentation", "Organization" ]
|
||||
|
||||
Set up the configuration file:
|
||||
|
||||
> task
|
||||
```bash
|
||||
task
|
||||
```
|
||||
|
||||
Add a task:
|
||||
|
||||
> task add *update linux*
|
||||
```bash
|
||||
task add update linux
|
||||
```
|
||||
|
||||
See which task is next:
|
||||
|
||||
> task next
|
||||
```bash
|
||||
task next
|
||||
```
|
||||
|
||||
Note the id number.
|
||||
|
||||
Mark a task as started:
|
||||
|
||||
> task start *1*
|
||||
```bash
|
||||
task start 1
|
||||
```
|
||||
|
||||
Once finished:
|
||||
|
||||
> task *1 done*
|
||||
```bash
|
||||
task 1 done
|
||||
```
|
||||
|
||||
# Projects
|
||||
|
||||
Add a project:
|
||||
|
||||
> task add project:*house buy potted plant*
|
||||
> task add proj:*house.repair buy screwdriver*
|
||||
> task add proj:*house.repair buy shelf brackets*
|
||||
3 task add pro:*house.paint buy white paint*
|
||||
> task add pro:*house.paint buy red paint*
|
||||
> task add pro:*house.paint buy black paint*
|
||||
> task add pro:*house.paint buy brushes*
|
||||
```bash
|
||||
task add project:house buy potted plant
|
||||
task add proj:house.repair buy screwdriver
|
||||
task add proj:house.repair buy shelf brackets
|
||||
task add pro:house.paint buy white paint
|
||||
task add pro:house.paint buy red paint
|
||||
task add pro:house.paint buy black paint
|
||||
task add pro:house.paint buy brushes
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
> task pro:house sum
|
||||
```bash
|
||||
task pro:house sum
|
||||
```
|
||||
|
||||
> task burndown.daily pro:house
|
||||
```bash
|
||||
task burndown.daily pro:house
|
||||
```
|
||||
|
||||
The summaries will show how fast a project is being completed, and when you can expect it to finish at the present rate.
|
||||
|
||||
# Tags
|
||||
|
||||
> task add +buy toothbrush
|
||||
```bash
|
||||
task add +buy toothbrush
|
||||
```
|
||||
|
||||
You can then see only tasks which involve buying something with:
|
||||
|
||||
> task +buy
|
||||
```bash
|
||||
task +buy
|
||||
```
|
||||
|
||||
# Contexts
|
||||
|
||||
Set three contexts by their tags:
|
||||
|
||||
> task context define *work +sa or +hr*
|
||||
```bash
|
||||
task context define work +sa or +hr
|
||||
```
|
||||
|
||||
> task context define *study +ed or +void or +rat*
|
||||
```bash
|
||||
task context define study +ed or +void or +rat
|
||||
```
|
||||
|
||||
> task context define *home -sa -hr -ed -void -rat*
|
||||
```bash
|
||||
task context define home -sa -hr -ed -void -rat
|
||||
```
|
||||
|
||||
Change to the first context.
|
||||
|
||||
> task context *work*
|
||||
```bash
|
||||
task context work
|
||||
```
|
||||
|
||||
Then stop.
|
||||
|
||||
> task context none
|
||||
```bash
|
||||
task context none
|
||||
```
|
||||
|
||||
# Review
|
||||
|
||||
View list of tasks completed in the last week:
|
||||
|
||||
> task end.after:today-1wk completed
|
||||
```bash
|
||||
task end.after:today-1wk completed
|
||||
```
|
||||
|
||||
# User Defined Attributes
|
||||
|
||||
Make a UDA 'size'.
|
||||
|
||||
> task config uda.size.type string
|
||||
```bash
|
||||
task config uda.size.type string
|
||||
```
|
||||
|
||||
> task config uda.size.label Size
|
||||
```bash
|
||||
task config uda.size.label Size
|
||||
```
|
||||
|
||||
> task config uda.size.values large,medium,small
|
||||
```bash
|
||||
task config uda.size.values large,medium,small
|
||||
```
|
||||
|
||||
> uda.size.default=medium
|
||||
```bash
|
||||
uda.size.default=medium
|
||||
```
|
||||
|
||||
# Tricks
|
||||
|
||||
This command shows tasks I'm most interested in:
|
||||
|
||||
> task next +ACTIVE or +OVERDUE or due:today or scheduled:today or pri:H
|
||||
```bash
|
||||
task next +ACTIVE or +OVERDUE or due:today or scheduled:today or pri:H
|
||||
```
|
||||
|
||||
The command is long, so `alias` is your friend.
|
||||
|
||||
|
@@ -6,76 +6,88 @@ tags: [ "Documentation", "Data" ]
|
||||
|
||||
Try:
|
||||
|
||||
> timew summary :yesterday
|
||||
```bash
|
||||
timew summary :yesterday
|
||||
```
|
||||
|
||||
You can also use :week, :lastweek, :month, :quarter, :year, or a range such as:
|
||||
|
||||
> timew summary today to tomorrow
|
||||
|
||||
> timew today - tomorrow
|
||||
|
||||
> 2018-10-15T06:00 - 2018-10-17T06:00
|
||||
```bash
|
||||
timew summary today to tomorrow
|
||||
timew today - tomorrow
|
||||
2018-10-15T06:00 - 2018-10-17T06:00
|
||||
```
|
||||
|
||||
Each of these can gain with the :ids tag.
|
||||
|
||||
# Basics
|
||||
|
||||
> timew start
|
||||
|
||||
> timew stop
|
||||
|
||||
> timew continue
|
||||
|
||||
> timew summary
|
||||
|
||||
> timew tags
|
||||
```bash
|
||||
timew start
|
||||
timew stop
|
||||
timew continue
|
||||
timew summary
|
||||
timew tags
|
||||
```
|
||||
|
||||
And add ids with:
|
||||
|
||||
> timew summary :ids
|
||||
|
||||
|
||||
> timew track 10am - 1pm timewarrior
|
||||
|
||||
> timew track 1pm for 2h walk
|
||||
```bash
|
||||
timew summary :ids
|
||||
timew track 10am - 1pm timewarrior
|
||||
timew track 1pm for 2h walk
|
||||
```
|
||||
|
||||
# Adjusting Timewarrior
|
||||
|
||||
First get ids.
|
||||
|
||||
> timew summary :ids
|
||||
```bash
|
||||
timew summary :ids
|
||||
```
|
||||
|
||||
Then if we're looking at task @2:
|
||||
|
||||
> timew move @2 12:00
|
||||
```bash
|
||||
timew move @2 12:00
|
||||
timew lengthen @2 3mins
|
||||
```
|
||||
|
||||
> timew lengthen @2 3mins
|
||||
|
||||
> time shorten @2 40mins
|
||||
```bash
|
||||
time shorten @2 40mins
|
||||
```
|
||||
|
||||
# Forgetting
|
||||
|
||||
> timew start 1h ago @4
|
||||
```bash
|
||||
timew start 1h ago @4
|
||||
```
|
||||
|
||||
Or if your action actually had a break:
|
||||
|
||||
> timew split @8
|
||||
```bash
|
||||
timew split @8
|
||||
```
|
||||
|
||||
Or maybe not?
|
||||
|
||||
> timew join @4 @8
|
||||
|
||||
> timew @8 delete
|
||||
```bash
|
||||
timew join @4 @8
|
||||
timew @8 delete
|
||||
```
|
||||
|
||||
Start at previous time
|
||||
|
||||
> timew start 3pm 'Read chapter 12'
|
||||
|
||||
> timew start 90mins ago 'Read chapter 12'
|
||||
```bash
|
||||
timew start 3pm 'Read chapter 12'
|
||||
timew start 90mins ago 'Read chapter 12'
|
||||
```
|
||||
|
||||
Cancel currently tracked time.
|
||||
|
||||
> timew cancel
|
||||
```bash
|
||||
timew cancel
|
||||
```
|
||||
|
||||
# Backdated tracking
|
||||
|
||||
@@ -129,25 +141,29 @@ task end.after:today-1wk completed
|
||||
|
||||
Replace
|
||||
|
||||
`os.system('timew start ' + combined + ' :yes')`
|
||||
> os.system('timew start ' + combined + ' :yes')
|
||||
|
||||
with:
|
||||
|
||||
`os.system('timew start ' + combined.decode() + ' :yes')`
|
||||
> os.system('timew start ' + combined.decode() + ' :yes')
|
||||
|
||||
and
|
||||
|
||||
`os.system('timew stop ' + combined + ' :yes')`
|
||||
> os.system('timew stop ' + combined + ' :yes')
|
||||
|
||||
with:
|
||||
|
||||
`os.system('timew stop ' + combined.decode() + ' :yes')`
|
||||
> os.system('timew stop ' + combined.decode() + ' :yes')
|
||||
|
||||
# Fixing Errors
|
||||
|
||||
> curl -O https://taskwarrior.org/download/timew-dbcorrection.py
|
||||
```bash
|
||||
curl -O https://taskwarrior.org/download/timew-dbcorrection.py
|
||||
```
|
||||
|
||||
> python timew-dbcorrections.py
|
||||
```bash
|
||||
python timew-dbcorrections.py
|
||||
```
|
||||
|
||||
# Setup
|
||||
|
||||
|
@@ -4,7 +4,9 @@ tags: [ "Documentation", "browsers" ]
|
||||
---
|
||||
Open a search tab:
|
||||
|
||||
> w3m ddg.gg
|
||||
```bash
|
||||
w3m ddg.gg
|
||||
```
|
||||
|
||||
<Tab> then enter to start typing.
|
||||
|
||||
|
Reference in New Issue
Block a user