mirror of
https://github.com/jbranchaud/til
synced 2026-03-04 15:08:45 +00:00
Compare commits
8 Commits
2cc52bf8bc
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c1f046d196 | ||
|
|
55a6691681 | ||
|
|
3632cfbe1b | ||
|
|
e82bc873b9 | ||
|
|
43ade88fab | ||
|
|
8f99085e4b | ||
|
|
f20428b06a | ||
|
|
e41802653d |
14
README.md
14
README.md
@@ -10,7 +10,7 @@ working across different projects via [VisualMode](https://www.visualmode.dev/).
|
|||||||
|
|
||||||
For a steady stream of TILs, [sign up for my newsletter](https://visualmode.kit.com/newsletter).
|
For a steady stream of TILs, [sign up for my newsletter](https://visualmode.kit.com/newsletter).
|
||||||
|
|
||||||
_1743 TILs and counting..._
|
_1751 TILs and counting..._
|
||||||
|
|
||||||
See some of the other learning resources I work on:
|
See some of the other learning resources I work on:
|
||||||
|
|
||||||
@@ -29,6 +29,7 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
* [Ansible](#ansible)
|
* [Ansible](#ansible)
|
||||||
* [Astro](#astro)
|
* [Astro](#astro)
|
||||||
* [AWS](#aws)
|
* [AWS](#aws)
|
||||||
|
* [Bash](#bash)
|
||||||
* [Brew](#brew)
|
* [Brew](#brew)
|
||||||
* [Chrome](#chrome)
|
* [Chrome](#chrome)
|
||||||
* [Claude Code](#claude-code)
|
* [Claude Code](#claude-code)
|
||||||
@@ -126,6 +127,10 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Turn Off Output Pager For A Command](aws/turn-off-output-pager-for-a-command.md)
|
- [Turn Off Output Pager For A Command](aws/turn-off-output-pager-for-a-command.md)
|
||||||
- [Use Specific AWS Profile With CLI](aws/use-specific-aws-profile-with-cli.md)
|
- [Use Specific AWS Profile With CLI](aws/use-specific-aws-profile-with-cli.md)
|
||||||
|
|
||||||
|
### Bash
|
||||||
|
|
||||||
|
- [Edit The Current Command Prompt](bash/edit-the-current-command-prompt.md)
|
||||||
|
|
||||||
### Brew
|
### Brew
|
||||||
|
|
||||||
- [Clean Up Your Brew Installations](brew/clean-up-your-brew-installations.md)
|
- [Clean Up Your Brew Installations](brew/clean-up-your-brew-installations.md)
|
||||||
@@ -241,6 +246,7 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Reload The nginx Configuration](devops/reload-the-nginx-configuration.md)
|
- [Reload The nginx Configuration](devops/reload-the-nginx-configuration.md)
|
||||||
- [Resolve The Public IP Of A URL](devops/resolve-the-public-ip-of-a-url.md)
|
- [Resolve The Public IP Of A URL](devops/resolve-the-public-ip-of-a-url.md)
|
||||||
- [Running Out Of inode Space](devops/running-out-of-inode-space.md)
|
- [Running Out Of inode Space](devops/running-out-of-inode-space.md)
|
||||||
|
- [Set, Get, And Unset Env Vars With Dokku](devops/set-get-and-unset-env-vars-with-dokku.md)
|
||||||
- [Set Up Domain For Hatchbox Rails App](devops/set-up-domain-for-hatchbox-rails-app.md)
|
- [Set Up Domain For Hatchbox Rails App](devops/set-up-domain-for-hatchbox-rails-app.md)
|
||||||
- [SSH Into A Docker Container](devops/ssh-into-a-docker-container.md)
|
- [SSH Into A Docker Container](devops/ssh-into-a-docker-container.md)
|
||||||
- [SSL Certificates Can Cover Multiple Domains](devops/ssl-certificates-can-cover-multiple-domains.md)
|
- [SSL Certificates Can Cover Multiple Domains](devops/ssl-certificates-can-cover-multiple-domains.md)
|
||||||
@@ -1033,12 +1039,16 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
### Python
|
### Python
|
||||||
|
|
||||||
- [Access Instance Variables](python/access-instance-variables.md)
|
- [Access Instance Variables](python/access-instance-variables.md)
|
||||||
|
- [Access Most Recent Return Value In REPL](python/access-most-recent-return-value-in-repl.md)
|
||||||
- [Break Debugger On First Line Of Program](python/break-debugger-on-first-line-of-program.md)
|
- [Break Debugger On First Line Of Program](python/break-debugger-on-first-line-of-program.md)
|
||||||
- [Check If Package Is Installed With Pip](python/check-if-package-is-installed-with-pip.md)
|
- [Check If Package Is Installed With Pip](python/check-if-package-is-installed-with-pip.md)
|
||||||
- [Create A Dummy DataFrame In Pandas](python/create-a-dummy-dataframe-in-pandas.md)
|
- [Create A Dummy DataFrame In Pandas](python/create-a-dummy-dataframe-in-pandas.md)
|
||||||
- [Dunder Methods](python/dunder-methods.md)
|
- [Dunder Methods](python/dunder-methods.md)
|
||||||
|
- [Easy Key-Value Aggregates With defaultdict](python/easy-key-value-aggregates-with-defaultdict.md)
|
||||||
- [Install With PIP For Specific Interpreter](python/install-with-pip-for-specific-interpreter.md)
|
- [Install With PIP For Specific Interpreter](python/install-with-pip-for-specific-interpreter.md)
|
||||||
- [Iterate First N Items From Enumerable](python/iterate-first-n-items-from-enumerable.md)
|
- [Iterate First N Items From Enumerable](python/iterate-first-n-items-from-enumerable.md)
|
||||||
|
- [Iterate Over A Dictionary](python/iterate-over-a-dictionary.md)
|
||||||
|
- [Keep A Tally With collections.Counter](python/keep-a-tally-with-collections-counter.md)
|
||||||
- [Load A File Into The Python REPL](python/load-a-file-into-the-python-repl.md)
|
- [Load A File Into The Python REPL](python/load-a-file-into-the-python-repl.md)
|
||||||
- [Override The Boolean Context Of A Class](python/override-the-boolean-context-of-a-class.md)
|
- [Override The Boolean Context Of A Class](python/override-the-boolean-context-of-a-class.md)
|
||||||
- [Store And Access Immutable Data In A Tuple](python/store-and-access-immutable-data-in-a-tuple.md)
|
- [Store And Access Immutable Data In A Tuple](python/store-and-access-immutable-data-in-a-tuple.md)
|
||||||
@@ -1639,6 +1649,7 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Check The Current Working Directory](unix/check-the-current-working-directory.md)
|
- [Check The Current Working Directory](unix/check-the-current-working-directory.md)
|
||||||
- [Check The Installed OpenSSL Version](unix/check-the-installed-openssl-version.md)
|
- [Check The Installed OpenSSL Version](unix/check-the-installed-openssl-version.md)
|
||||||
- [Clear The Screen](unix/clear-the-screen.md)
|
- [Clear The Screen](unix/clear-the-screen.md)
|
||||||
|
- [Combine All My TILs Into A Single File](unix/combine-all-my-tils-into-a-single-file.md)
|
||||||
- [Command Line Length Limitations](unix/command-line-length-limitations.md)
|
- [Command Line Length Limitations](unix/command-line-length-limitations.md)
|
||||||
- [Compare Two Variables In A Bash Script](unix/compare-two-variables-in-a-bash-script.md)
|
- [Compare Two Variables In A Bash Script](unix/compare-two-variables-in-a-bash-script.md)
|
||||||
- [Configure cd To Behave Like pushd In Zsh](unix/configure-cd-to-behave-like-pushd-in-zsh.md)
|
- [Configure cd To Behave Like pushd In Zsh](unix/configure-cd-to-behave-like-pushd-in-zsh.md)
|
||||||
@@ -1710,6 +1721,7 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Ignore A Directory During ripgrep Search](unix/ignore-a-directory-during-ripgrep-search.md)
|
- [Ignore A Directory During ripgrep Search](unix/ignore-a-directory-during-ripgrep-search.md)
|
||||||
- [Ignore The Alias When Running A Command](unix/ignore-the-alias-when-running-a-command.md)
|
- [Ignore The Alias When Running A Command](unix/ignore-the-alias-when-running-a-command.md)
|
||||||
- [Include Ignore Files In Ripgrep Search](unix/include-ignore-files-in-ripgrep-search.md)
|
- [Include Ignore Files In Ripgrep Search](unix/include-ignore-files-in-ripgrep-search.md)
|
||||||
|
- [Inspect EXIF Data For An Image File](unix/inspect-exif-data-for-an-image-file.md)
|
||||||
- [Interactively Browse Available Node Versions](unix/interactively-browse-availabile-node-versions.md)
|
- [Interactively Browse Available Node Versions](unix/interactively-browse-availabile-node-versions.md)
|
||||||
- [Interactively Switch asdf Package Versions](unix/interactively-switch-asdf-package-versions.md)
|
- [Interactively Switch asdf Package Versions](unix/interactively-switch-asdf-package-versions.md)
|
||||||
- [Interpret Cron Schedule From The CLI](unix/interpret-cron-schedule-from-the-cli.md)
|
- [Interpret Cron Schedule From The CLI](unix/interpret-cron-schedule-from-the-cli.md)
|
||||||
|
|||||||
18
bash/edit-the-current-command-prompt.md
Normal file
18
bash/edit-the-current-command-prompt.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Edit The Current Command Prompt
|
||||||
|
|
||||||
|
A neat feature of `bash` is the ability to open whatever the current state of
|
||||||
|
the command prompt is into your default editor.
|
||||||
|
|
||||||
|
Let's say we have a really long command that we've just tried to run, but it
|
||||||
|
failed and we need to make a small change somewhere in the middle. Instead of
|
||||||
|
holding the left arrow key for 30 seconds, we can instead hit `CTRL-X CTRL-E`.
|
||||||
|
|
||||||
|
This pops us into our `EDITOR` (or maybe `VISUAL`, not sure which). In my case,
|
||||||
|
that is `nvim`. I now have access to all the features I'm used to in `nvim` for
|
||||||
|
quickly navigating to and editing, searching and replacing, or whatever.
|
||||||
|
|
||||||
|
Once I've got the command how I like it, I can save and exit (`:wq`) and the
|
||||||
|
updated command will be executed.
|
||||||
|
|
||||||
|
This is similar to [the `fc` builtin](unix/fix-previous-command-with-fc.md),
|
||||||
|
which also happens to be available for `zsh`.
|
||||||
29
devops/set-get-and-unset-env-vars-with-dokku.md
Normal file
29
devops/set-get-and-unset-env-vars-with-dokku.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Set, Get, And Unset Env Vars With Dokku
|
||||||
|
|
||||||
|
The `dokku` CLI provides `config` subcommands for managing environment variables
|
||||||
|
for the target container.
|
||||||
|
|
||||||
|
An env var can be set for an active container with `config:set`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ dokku config:set app-name JEMALLOC_ENABLED=true MALLOC_CONF="stats_print:true"
|
||||||
|
```
|
||||||
|
|
||||||
|
Notice I'm able to set multiple env vars at once if needed.
|
||||||
|
|
||||||
|
If I ever need to check what an env var is currently set to for one of my app
|
||||||
|
containers, I can use `config:get`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ dokku config:get app-name JEMALLOC_ENABLED
|
||||||
|
true
|
||||||
|
```
|
||||||
|
|
||||||
|
I can always override any value with another `config:set`. However, if I need to
|
||||||
|
entirely remove the env var, I can use `config:unset`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ dokku config:unset app-name MALLOC_CONF
|
||||||
|
```
|
||||||
|
|
||||||
|
[source](https://dokku.com/docs/configuration/environment-variables/)
|
||||||
34
python/access-most-recent-return-value-in-repl.md
Normal file
34
python/access-most-recent-return-value-in-repl.md
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# Access Most Recent Return Value In REPL
|
||||||
|
|
||||||
|
One of my favorite features of Ruby's `irb` and `pry` are that you can use `_`
|
||||||
|
to reference the most recent return value. Often as we use an interpreter or
|
||||||
|
REPL, we end up with _intermediate_ values. That is, we've execute some kind of
|
||||||
|
statement which returned a value and we now want to use that resulting value in
|
||||||
|
our next statement. Python also supports `_`.
|
||||||
|
|
||||||
|
Let's say I've run a statement that took a while to process, but I forgot to
|
||||||
|
assign it to a variable. Instead of re-running the whole thing, I can create a
|
||||||
|
variable that references the previous return value using `_`.
|
||||||
|
|
||||||
|
```python
|
||||||
|
>>> BytePairEncoding.train_bpe(long_text)
|
||||||
|
{'merge_rules': [...], 'vocab': {...}}
|
||||||
|
>>> result = _
|
||||||
|
>>> list(result.keys())
|
||||||
|
['merge_rules', 'vocab']
|
||||||
|
```
|
||||||
|
|
||||||
|
Even if I don't necessarily want to assign it a variable, it can be nice to
|
||||||
|
reference the previous value as I continue with what I'm doing:
|
||||||
|
|
||||||
|
```python
|
||||||
|
>>> result['merge_rules'][0][1]
|
||||||
|
256
|
||||||
|
>>> result['vocab'][_]
|
||||||
|
b'e '
|
||||||
|
```
|
||||||
|
|
||||||
|
Notice how the value from the first statement gets used as part of a `dict`
|
||||||
|
access.
|
||||||
|
|
||||||
|
[source](https://docs.python.org/3/tutorial/introduction.html#numbers)
|
||||||
53
python/easy-key-value-aggregates-with-defaultdict.md
Normal file
53
python/easy-key-value-aggregates-with-defaultdict.md
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
# Easy Key-Value Aggregates With defaultdict
|
||||||
|
|
||||||
|
The `collections` module has the `defaultdict` object that can be used to
|
||||||
|
aggregate values tied to a key. What sets this apart from simply using a `dict`
|
||||||
|
is that we get the base value for free. So if our aggregate value is a list,
|
||||||
|
then we get `[]` by default for each new key. In the same way, we'd get `0` if
|
||||||
|
it was constructed with `int`.
|
||||||
|
|
||||||
|
Here is the counter example from [Keep A Tally With
|
||||||
|
collections.Counter](keep-a-tally-with-collections-counter.md)
|
||||||
|
|
||||||
|
```python
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
def get_pair_counts(token_ids: list[int]) -> Counter:
|
||||||
|
"""Count how often each adjacent pair appears"""
|
||||||
|
counts = defaultdict(int)
|
||||||
|
for i in range(len(token_ids) - 1):
|
||||||
|
pair = (token_ids[i], token_ids[i + 1])
|
||||||
|
counts[pair] += 1
|
||||||
|
return counts
|
||||||
|
```
|
||||||
|
|
||||||
|
We never have to initially set a key to `0`. If the key is not yet present, then
|
||||||
|
`int()` (the zero-value constructor) is used as the `__missing__` value.
|
||||||
|
|
||||||
|
We can do the same with `list`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
>>> import collections
|
||||||
|
>>> stuff = collections.defaultdict(list)
|
||||||
|
>>> stuff['alpha'].append(1)
|
||||||
|
>>> stuff['alpha']
|
||||||
|
[1]
|
||||||
|
>>> stuff['beta']
|
||||||
|
[]
|
||||||
|
```
|
||||||
|
|
||||||
|
In the same way, this uses `list()` as the `__missing__` value to start of each
|
||||||
|
key with an `[]`.
|
||||||
|
|
||||||
|
I find this so handy because in other languages I've typically had to do
|
||||||
|
something more like this:
|
||||||
|
|
||||||
|
```python
|
||||||
|
words_by_length = {}
|
||||||
|
for item in items:
|
||||||
|
if len(item) not in words_by_length:
|
||||||
|
words_by_length[len(item)] = []
|
||||||
|
words_by_length[len(item)].append(item)
|
||||||
|
```
|
||||||
|
|
||||||
|
This is much clunkier.
|
||||||
34
python/iterate-over-a-dictionary.md
Normal file
34
python/iterate-over-a-dictionary.md
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# Iterate Over A Dictionary
|
||||||
|
|
||||||
|
Let's say we have a `dict` that contains counts of occurrences for each word in
|
||||||
|
some sample text:
|
||||||
|
|
||||||
|
```python
|
||||||
|
words_frequency = {
|
||||||
|
"the": 4,
|
||||||
|
"a": 3,
|
||||||
|
"dog": 1,
|
||||||
|
"bone": 1,
|
||||||
|
"wants": 1,
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Here is how we can iterate over the `dict`, accessing both the keys and values:
|
||||||
|
|
||||||
|
```python
|
||||||
|
for word, count in word_frequency.items():
|
||||||
|
print(f"- {word} appears {count} time{'' if count == 1 else 's'}")
|
||||||
|
```
|
||||||
|
|
||||||
|
Using the
|
||||||
|
[`items()`](https://docs.python.org/3/library/stdtypes.html#dict.items) method,
|
||||||
|
we're able to access both _key_ and _value_ with the for loop as it iterates.
|
||||||
|
|
||||||
|
Another approach is to loop directly on the `dict` which implicitly surfaces the
|
||||||
|
_key_ for iteration. This can then be used to get the value from the `dict`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
for word in word_frequency:
|
||||||
|
print(f"- {word}: {word_frequency[word]}
|
||||||
|
```
|
||||||
40
python/keep-a-tally-with-collections-counter.md
Normal file
40
python/keep-a-tally-with-collections-counter.md
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# Keep A Tally With collections.Counter
|
||||||
|
|
||||||
|
Python's `collections` module comes with a
|
||||||
|
[`Counter`](https://docs.python.org/3/library/collections.html#collections.Counter)
|
||||||
|
object which is a specialized dict subclass focussed on tallying counts of keys.
|
||||||
|
|
||||||
|
> It is a collection where elements are stored as dictionary keys and their
|
||||||
|
> counts are stored as dictionary values. Counts are allowed to be any integer
|
||||||
|
> value including zero or negative counts.
|
||||||
|
|
||||||
|
I used it recently while doing an exploratory implementation of a Byte-Pair
|
||||||
|
Encoding (BPE):
|
||||||
|
|
||||||
|
```python
|
||||||
|
from collections import Counter
|
||||||
|
|
||||||
|
def get_pair_counts(token_ids: list[int]) -> Counter:
|
||||||
|
"""Count how often each adjacent pair appears"""
|
||||||
|
counts = Counter()
|
||||||
|
for i in range(len(token_ids) - 1):
|
||||||
|
pair = (token_ids[i], token_ids[i + 1])
|
||||||
|
counts[pair] += 1
|
||||||
|
return counts
|
||||||
|
```
|
||||||
|
|
||||||
|
Here I'm able to count the number of occurrences of each pair of bytes from the
|
||||||
|
input text. A tuple of `int` values is hashable, so they work great as keys for
|
||||||
|
a `Counter`.
|
||||||
|
|
||||||
|
The count value of any key will default to `0`. That makes it straightforward to
|
||||||
|
increment from there as you iterating over occurrences.
|
||||||
|
|
||||||
|
```python
|
||||||
|
>>> counts = Counter()
|
||||||
|
>>> counts['hello']
|
||||||
|
0
|
||||||
|
>>> count['hello'] += 1
|
||||||
|
>>> count['hello']
|
||||||
|
1
|
||||||
|
```
|
||||||
35
unix/combine-all-my-tils-into-a-single-file.md
Normal file
35
unix/combine-all-my-tils-into-a-single-file.md
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# Combine All My TILs Into A Single File
|
||||||
|
|
||||||
|
In [Build A Small Text-based Training
|
||||||
|
Dataset](https://www.visualmode.dev/build-a-small-text-training-dataset), I went
|
||||||
|
over my need for a sizeable and interesting corpus of text that I could use as a
|
||||||
|
training dataset I could use to run against [my own naive Byte Pair Encoding
|
||||||
|
implementation](https://github.com/jbranchaud/build-an-llm-from-scratch/blob/main/chapter-02/bpe_tokenizer.py).
|
||||||
|
My repo of hand-written TILs is a great candidate, but I need those smashed all
|
||||||
|
into one file.
|
||||||
|
|
||||||
|
Here is a formatted version of the one-liner I ended up with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
{
|
||||||
|
cat README.md; \
|
||||||
|
find */ -name '*.md' -print0 \
|
||||||
|
| sort -z \
|
||||||
|
| xargs -0 -I{} sh -c 'echo "<|endoftext|>"; cat "$1"' _ {}; \
|
||||||
|
} > combined.md
|
||||||
|
```
|
||||||
|
|
||||||
|
This combines all 1700+ of my TILs into a single file separated by the
|
||||||
|
`<|endoftext|>` delimiter.
|
||||||
|
|
||||||
|
The two things I find most interesting about this command are:
|
||||||
|
|
||||||
|
1. The use of a null byte (`\0`) separator between the filenames in case there
|
||||||
|
is anything weird (like spaces) in those filenames. This starts with
|
||||||
|
`-print0`. The `-z` of `sort` maintains that null byte separator. And then
|
||||||
|
`xargs` knows to handle it by the `-0` flag.
|
||||||
|
|
||||||
|
2. We can coerce `xargs` into running multiple commands by having it spawn a
|
||||||
|
single shell process that runs each of those commands. To reliably pass the
|
||||||
|
filename into that shell process, we have `xargs` constitute it as the second
|
||||||
|
argument (`$1`) by substituting in the filename where `{}` appears.
|
||||||
47
unix/inspect-exif-data-for-an-image-file.md
Normal file
47
unix/inspect-exif-data-for-an-image-file.md
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# Inspect EXIF Data For An Image File
|
||||||
|
|
||||||
|
The `exiftool` CLI (which can be downloaded via `brew`) is a useful tool for
|
||||||
|
inspecting all the EXIF data attached to a media file. A media file like an
|
||||||
|
image has a bunch of additional details embedded in it like timestamps, image
|
||||||
|
metadata, and sometimes location information.
|
||||||
|
|
||||||
|
Here is all the data attached to a screenshot I found on my desktop:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
❯ exiftool ~/Desktop/CleanShot\ 2025-11-17\ at\ 11.22.18@2x.png
|
||||||
|
ExifTool Version Number : 13.50
|
||||||
|
File Name : CleanShot 2025-11-17 at 11.22.18@2x.png
|
||||||
|
Directory : /Users/lastword/Desktop
|
||||||
|
File Size : 1194 kB
|
||||||
|
File Modification Date/Time : 2025:11:17 11:22:21-06:00
|
||||||
|
File Access Date/Time : 2025:12:15 10:43:55-06:00
|
||||||
|
File Inode Change Date/Time : 2025:12:05 15:37:48-06:00
|
||||||
|
File Permissions : -rw-r--r--
|
||||||
|
File Type : PNG
|
||||||
|
File Type Extension : png
|
||||||
|
MIME Type : image/png
|
||||||
|
Image Width : 2502
|
||||||
|
Image Height : 1232
|
||||||
|
Bit Depth : 8
|
||||||
|
Color Type : RGB with Alpha
|
||||||
|
Compression : Deflate/Inflate
|
||||||
|
Filter : Adaptive
|
||||||
|
Interlace : Noninterlaced
|
||||||
|
XMP Toolkit : XMP Core 6.0.0
|
||||||
|
Y Resolution : 144
|
||||||
|
Resolution Unit : inches
|
||||||
|
X Resolution : 144
|
||||||
|
Exif Image Width : 2502
|
||||||
|
Color Space : sRGB
|
||||||
|
User Comment : Screenshot
|
||||||
|
Exif Image Height : 1232
|
||||||
|
SRGB Rendering : Perceptual
|
||||||
|
Image Size : 2502x1232
|
||||||
|
Megapixels : 3.1
|
||||||
|
```
|
||||||
|
|
||||||
|
This works with other kinds of media files. For instance, I ran this against an
|
||||||
|
MP4 screen recording file which contained even more metadata.
|
||||||
|
|
||||||
|
In addition to reading data, `exiftool` can also write it. See `man exiftool`
|
||||||
|
for more details on what else it can do.
|
||||||
Reference in New Issue
Block a user