mirror of
https://github.com/jbranchaud/til
synced 2026-01-20 15:38:02 +00:00
Compare commits
1 Commits
db0530eb90
...
4369bf392e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4369bf392e |
23
README.md
23
README.md
@@ -10,7 +10,7 @@ pairing with smart people at Hashrocket.
|
|||||||
|
|
||||||
For a steady stream of TILs, [sign up for my newsletter](https://crafty-builder-6996.ck.page/e169c61186).
|
For a steady stream of TILs, [sign up for my newsletter](https://crafty-builder-6996.ck.page/e169c61186).
|
||||||
|
|
||||||
_1531 TILs and counting..._
|
_1514 TILs and counting..._
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -41,7 +41,6 @@ _1531 TILs and counting..._
|
|||||||
* [Internet](#internet)
|
* [Internet](#internet)
|
||||||
* [Java](#java)
|
* [Java](#java)
|
||||||
* [JavaScript](#javascript)
|
* [JavaScript](#javascript)
|
||||||
* [jj](#jj)
|
|
||||||
* [jq](#jq)
|
* [jq](#jq)
|
||||||
* [Kitty](#kitty)
|
* [Kitty](#kitty)
|
||||||
* [Linux](#linux)
|
* [Linux](#linux)
|
||||||
@@ -311,7 +310,6 @@ _1531 TILs and counting..._
|
|||||||
- [Find And Remove Files That Match A Name](git/find-and-remove-files-that-match-a-name.md)
|
- [Find And Remove Files That Match A Name](git/find-and-remove-files-that-match-a-name.md)
|
||||||
- [Find The Date That A File Was Added To The Repo](git/find-the-date-that-a-file-was-added-to-the-repo.md)
|
- [Find The Date That A File Was Added To The Repo](git/find-the-date-that-a-file-was-added-to-the-repo.md)
|
||||||
- [Find The Initial Commit](git/find-the-initial-commit.md)
|
- [Find The Initial Commit](git/find-the-initial-commit.md)
|
||||||
- [Get Latest Commit Timestamp For A File](git/get-latest-commit-timestamp-for-a-file.md)
|
|
||||||
- [Get The Name Of The Current Branch](git/get-the-name-of-the-current-branch.md)
|
- [Get The Name Of The Current Branch](git/get-the-name-of-the-current-branch.md)
|
||||||
- [Get The Short Version Of The Latest Commit](git/get-the-short-version-of-the-latest-commit.md)
|
- [Get The Short Version Of The Latest Commit](git/get-the-short-version-of-the-latest-commit.md)
|
||||||
- [Grab A Single File From A Stash](git/grab-a-single-file-from-a-stash.md)
|
- [Grab A Single File From A Stash](git/grab-a-single-file-from-a-stash.md)
|
||||||
@@ -399,12 +397,9 @@ _1531 TILs and counting..._
|
|||||||
|
|
||||||
- [Access Go Docs Offline](go/access-go-docs-offline.md)
|
- [Access Go Docs Offline](go/access-go-docs-offline.md)
|
||||||
- [Build For A Specific OS And Architecture](go/build-for-a-specific-os-and-architecture.md)
|
- [Build For A Specific OS And Architecture](go/build-for-a-specific-os-and-architecture.md)
|
||||||
- [Combine Two Slices](go/combine-two-slices.md)
|
|
||||||
- [Do Something N Times](go/do-something-n-times.md)
|
- [Do Something N Times](go/do-something-n-times.md)
|
||||||
- [Find Executables Installed By Go](go/find-executables-installed-by-go.md)
|
- [Find Executables Installed By Go](go/find-executables-installed-by-go.md)
|
||||||
- [Not So Random](go/not-so-random.md)
|
- [Not So Random](go/not-so-random.md)
|
||||||
- [Parse A String Into Individual Fields](go/parse-a-string-into-individual-fields.md)
|
|
||||||
- [Parse Flags From CLI Arguments](go/parse-flags-from-cli-arguments.md)
|
|
||||||
- [Replace The Current Process With An External Command](go/replace-the-current-process-with-an-external-command.md)
|
- [Replace The Current Process With An External Command](go/replace-the-current-process-with-an-external-command.md)
|
||||||
- [Sleep For A Duration](go/sleep-for-a-duration.md)
|
- [Sleep For A Duration](go/sleep-for-a-duration.md)
|
||||||
- [Upgrading From An Older Version On Mac](go/upgrading-from-an-older-version-on-mac.md)
|
- [Upgrading From An Older Version On Mac](go/upgrading-from-an-older-version-on-mac.md)
|
||||||
@@ -460,7 +455,6 @@ _1531 TILs and counting..._
|
|||||||
- [Get Random Images From Unsplash](internet/get-random-images-from-unsplash.md)
|
- [Get Random Images From Unsplash](internet/get-random-images-from-unsplash.md)
|
||||||
- [Search Tweets By Author](internet/search-tweets-by-author.md)
|
- [Search Tweets By Author](internet/search-tweets-by-author.md)
|
||||||
- [Show All Pivotal Stories With Blockers](internet/show-all-pivotal-stories-with-blockers.md)
|
- [Show All Pivotal Stories With Blockers](internet/show-all-pivotal-stories-with-blockers.md)
|
||||||
- [Verify Site Ownership With DNS Record](internet/verify-site-ownership-with-dns-record.md)
|
|
||||||
|
|
||||||
### Java
|
### Java
|
||||||
|
|
||||||
@@ -574,11 +568,6 @@ _1531 TILs and counting..._
|
|||||||
- [Yarn Commands Without The Emojis](javascript/yarn-commands-without-the-emojis.md)
|
- [Yarn Commands Without The Emojis](javascript/yarn-commands-without-the-emojis.md)
|
||||||
- [Yup Schemas Are Validated Asynchronously](javascript/yup-schemas-are-validated-asynchronously.md)
|
- [Yup Schemas Are Validated Asynchronously](javascript/yup-schemas-are-validated-asynchronously.md)
|
||||||
|
|
||||||
### jj
|
|
||||||
|
|
||||||
- [Colocate jj And git Directories For Project](jj/colocate-jj-and-git-directories-for-project.md)
|
|
||||||
- [Find System-wide Config File For User](jj/find-system-wide-config-file-for-user.md)
|
|
||||||
|
|
||||||
### jq
|
### jq
|
||||||
|
|
||||||
- [Combine An Array Of Objects Into A Single Object](jq/combine-an-array-of-objects-into-a-single-object.md)
|
- [Combine An Array Of Objects Into A Single Object](jq/combine-an-array-of-objects-into-a-single-object.md)
|
||||||
@@ -641,7 +630,6 @@ _1531 TILs and counting..._
|
|||||||
- [Specify App When Opening From Command Line](mac/specify-app-when-opening-from-command-line.md)
|
- [Specify App When Opening From Command Line](mac/specify-app-when-opening-from-command-line.md)
|
||||||
- [Use Default Screenshot Shortcuts With CleanShot X](mac/use-default-screenshot-shortcuts-with-cleanshot-x.md)
|
- [Use Default Screenshot Shortcuts With CleanShot X](mac/use-default-screenshot-shortcuts-with-cleanshot-x.md)
|
||||||
- [View All Windows Of The Current App](mac/view-all-windows-of-the-current-app.md)
|
- [View All Windows Of The Current App](mac/view-all-windows-of-the-current-app.md)
|
||||||
- [Write System Clipboard To A File](mac/write-system-clipboard-to-a-file.md)
|
|
||||||
|
|
||||||
### MongoDB
|
### MongoDB
|
||||||
|
|
||||||
@@ -692,7 +680,6 @@ _1531 TILs and counting..._
|
|||||||
- [Fetch Does Not Work In API Serverless Function](nextjs/fetch-does-not-work-in-api-serverless-function.md)
|
- [Fetch Does Not Work In API Serverless Function](nextjs/fetch-does-not-work-in-api-serverless-function.md)
|
||||||
- [Make Environment Variable Publicly Available](nextjs/make-environment-variable-publicly-available.md)
|
- [Make Environment Variable Publicly Available](nextjs/make-environment-variable-publicly-available.md)
|
||||||
- [Match Middleware On Groups Of Paths](nextjs/match-middleware-on-groups-of-paths.md)
|
- [Match Middleware On Groups Of Paths](nextjs/match-middleware-on-groups-of-paths.md)
|
||||||
- [Organize Pages In Route Groups](nextjs/organize-pages-in-route-groups.md)
|
|
||||||
- [Precedence Of Dot Env Files](nextjs/precedence-of-dot-env-files.md)
|
- [Precedence Of Dot Env Files](nextjs/precedence-of-dot-env-files.md)
|
||||||
- [Push A Route With A URL Object](nextjs/push-a-route-with-a-url-object.md)
|
- [Push A Route With A URL Object](nextjs/push-a-route-with-a-url-object.md)
|
||||||
- [Redirect An Unauthorized User](nextjs/redirect-an-unauthorized-user.md)
|
- [Redirect An Unauthorized User](nextjs/redirect-an-unauthorized-user.md)
|
||||||
@@ -780,7 +767,6 @@ _1531 TILs and counting..._
|
|||||||
- [Escaping String Literals With Dollar Quoting](postgres/escaping-string-literals-with-dollar-quoting.md)
|
- [Escaping String Literals With Dollar Quoting](postgres/escaping-string-literals-with-dollar-quoting.md)
|
||||||
- [Export Query Results To A CSV](postgres/export-query-results-to-a-csv.md)
|
- [Export Query Results To A CSV](postgres/export-query-results-to-a-csv.md)
|
||||||
- [Extracting Nested JSON Data](postgres/extracting-nested-json-data.md)
|
- [Extracting Nested JSON Data](postgres/extracting-nested-json-data.md)
|
||||||
- [Fetch Specific Number Of Results](postgres/fetch-specific-number-of-results.md)
|
|
||||||
- [Find Duplicate Records In Table Without Unique Id](postgres/find-duplicate-records-in-table-without-unique-id.md)
|
- [Find Duplicate Records In Table Without Unique Id](postgres/find-duplicate-records-in-table-without-unique-id.md)
|
||||||
- [Find Records That Contain Duplicate Values](postgres/find-records-that-contain-duplicate-values.md)
|
- [Find Records That Contain Duplicate Values](postgres/find-records-that-contain-duplicate-values.md)
|
||||||
- [Find Records That Have Multiple Associated Records](postgres/find-records-that-have-multiple-associated-records.md)
|
- [Find Records That Have Multiple Associated Records](postgres/find-records-that-have-multiple-associated-records.md)
|
||||||
@@ -829,7 +815,6 @@ _1531 TILs and counting..._
|
|||||||
- [Manage Major Versions With Brew And Direnv](postgres/manage-major-versions-with-brew-and-direnv.md)
|
- [Manage Major Versions With Brew And Direnv](postgres/manage-major-versions-with-brew-and-direnv.md)
|
||||||
- [Max Identifier Length Is 63 Bytes](postgres/max-identifier-length-is-63-bytes.md)
|
- [Max Identifier Length Is 63 Bytes](postgres/max-identifier-length-is-63-bytes.md)
|
||||||
- [Open Heroku Database In Postico From Terminal](postgres/open-heroku-database-in-postico-from-terminal.md)
|
- [Open Heroku Database In Postico From Terminal](postgres/open-heroku-database-in-postico-from-terminal.md)
|
||||||
- [Output Explain Query Plan In Different Formats](postgres/output-explain-query-plan-in-different-formats.md)
|
|
||||||
- [pg Prefix Is Reserved For System Schemas](postgres/pg-prefix-is-reserved-for-system-schemas.md)
|
- [pg Prefix Is Reserved For System Schemas](postgres/pg-prefix-is-reserved-for-system-schemas.md)
|
||||||
- [Postgres Does Not Support Unsigned Integers](postgres/postgres-does-not-support-unsigned-integers.md)
|
- [Postgres Does Not Support Unsigned Integers](postgres/postgres-does-not-support-unsigned-integers.md)
|
||||||
- [Prepare, Execute, And Deallocate Statements](postgres/prepare-execute-and-deallocate-statements.md)
|
- [Prepare, Execute, And Deallocate Statements](postgres/prepare-execute-and-deallocate-statements.md)
|
||||||
@@ -1197,7 +1182,6 @@ _1531 TILs and counting..._
|
|||||||
- [Audit Your Ruby Project For Any CVEs](ruby/audit-your-ruby-project-for-any-cves.md)
|
- [Audit Your Ruby Project For Any CVEs](ruby/audit-your-ruby-project-for-any-cves.md)
|
||||||
- [Assoc For Hashes](ruby/assoc-for-hashes.md)
|
- [Assoc For Hashes](ruby/assoc-for-hashes.md)
|
||||||
- [Block Comments](ruby/block-comments.md)
|
- [Block Comments](ruby/block-comments.md)
|
||||||
- [Block Syntaxes Have Different Precedence](ruby/block-syntaxes-have-different-precedence.md)
|
|
||||||
- [Build HTTP And HTTPS URLs](ruby/build-http-and-https-urls.md)
|
- [Build HTTP And HTTPS URLs](ruby/build-http-and-https-urls.md)
|
||||||
- [Chaining Multiple RSpec Change Matchers](ruby/chaining-multiple-rspec-change-matchers.md)
|
- [Chaining Multiple RSpec Change Matchers](ruby/chaining-multiple-rspec-change-matchers.md)
|
||||||
- [Check For Any Overlaps In List Of Ranges](ruby/check-for-any-overlaps-in-list-of-ranges.md)
|
- [Check For Any Overlaps In List Of Ranges](ruby/check-for-any-overlaps-in-list-of-ranges.md)
|
||||||
@@ -1241,7 +1225,6 @@ _1531 TILs and counting..._
|
|||||||
- [Finding The Source of Ruby Methods](ruby/finding-the-source-of-ruby-methods.md)
|
- [Finding The Source of Ruby Methods](ruby/finding-the-source-of-ruby-methods.md)
|
||||||
- [Format A Hash Into A String Template](ruby/format-a-hash-into-a-string-template.md)
|
- [Format A Hash Into A String Template](ruby/format-a-hash-into-a-string-template.md)
|
||||||
- [Forward All Arguments To Another Method](ruby/forward-all-arguments-to-another-method.md)
|
- [Forward All Arguments To Another Method](ruby/forward-all-arguments-to-another-method.md)
|
||||||
- [Gather Positional Arguments In Method Definition](ruby/gather-positional-arguments-in-method-definition.md)
|
|
||||||
- [Generate A Signed JWT Token](ruby/generate-a-signed-jwt-token.md)
|
- [Generate A Signed JWT Token](ruby/generate-a-signed-jwt-token.md)
|
||||||
- [Generate Ruby Version And Gemset Files With RVM](ruby/generate-ruby-version-and-gemset-files-with-rvm.md)
|
- [Generate Ruby Version And Gemset Files With RVM](ruby/generate-ruby-version-and-gemset-files-with-rvm.md)
|
||||||
- [Get Info About Your RubyGems Environment](ruby/get-info-about-your-ruby-gems-environment.md)
|
- [Get Info About Your RubyGems Environment](ruby/get-info-about-your-ruby-gems-environment.md)
|
||||||
@@ -1309,7 +1292,6 @@ _1531 TILs and counting..._
|
|||||||
- [Specify How Random Array#sample Is](ruby/specify-how-random-array-sample-is.md)
|
- [Specify How Random Array#sample Is](ruby/specify-how-random-array-sample-is.md)
|
||||||
- [Split A Float Into Its Integer And Decimal](ruby/split-a-float-into-its-integer-and-decimal.md)
|
- [Split A Float Into Its Integer And Decimal](ruby/split-a-float-into-its-integer-and-decimal.md)
|
||||||
- [Squeeze Out The Extra Space](ruby/squeeze-out-the-extra-space.md)
|
- [Squeeze Out The Extra Space](ruby/squeeze-out-the-extra-space.md)
|
||||||
- [Stack Heredocs In A Method Call](ruby/stack-heredocs-in-a-method-call.md)
|
|
||||||
- [String Interpolation With Instance Variables](ruby/string-interpolation-with-instance-variables.md)
|
- [String Interpolation With Instance Variables](ruby/string-interpolation-with-instance-variables.md)
|
||||||
- [Summing Collections](ruby/summing-collections.md)
|
- [Summing Collections](ruby/summing-collections.md)
|
||||||
- [Triple Equals: The Case Equality Operator](ruby/triple-equals-the-case-equality-operator.md)
|
- [Triple Equals: The Case Equality Operator](ruby/triple-equals-the-case-equality-operator.md)
|
||||||
@@ -1513,7 +1495,6 @@ _1531 TILs and counting..._
|
|||||||
- [List Stats For A File](unix/list-stats-for-a-file.md)
|
- [List Stats For A File](unix/list-stats-for-a-file.md)
|
||||||
- [List The Available JDKs](unix/list-the-available-jdks.md)
|
- [List The Available JDKs](unix/list-the-available-jdks.md)
|
||||||
- [List The Stack Of Remembered Directories](unix/list-the-stack-of-remembered-directories.md)
|
- [List The Stack Of Remembered Directories](unix/list-the-stack-of-remembered-directories.md)
|
||||||
- [List TXT DNS Records For A Domain](unix/list-txt-dns-records-for-a-domain.md)
|
|
||||||
- [Load Env Vars In Bash Script](unix/load-env-vars-in-bash-script.md)
|
- [Load Env Vars In Bash Script](unix/load-env-vars-in-bash-script.md)
|
||||||
- [Look Through All Files That Have Been Git Stashed](unix/look-through-all-files-that-have-been-git-stashed.md)
|
- [Look Through All Files That Have Been Git Stashed](unix/look-through-all-files-that-have-been-git-stashed.md)
|
||||||
- [Make Direnv Less Noisy](unix/make-direnv-less-noisy.md)
|
- [Make Direnv Less Noisy](unix/make-direnv-less-noisy.md)
|
||||||
@@ -1556,7 +1537,6 @@ _1531 TILs and counting..._
|
|||||||
- [Switch Versions of a Brew Formula](unix/switch-versions-of-a-brew-formula.md)
|
- [Switch Versions of a Brew Formula](unix/switch-versions-of-a-brew-formula.md)
|
||||||
- [Tell direnv To Load The Env File](unix/tell-direnv-to-load-the-env-file.md)
|
- [Tell direnv To Load The Env File](unix/tell-direnv-to-load-the-env-file.md)
|
||||||
- [Touch Access And Modify Times Individually](unix/touch-access-and-modify-times-individually.md)
|
- [Touch Access And Modify Times Individually](unix/touch-access-and-modify-times-individually.md)
|
||||||
- [Type Fewer Paths With Brace Expansion](unix/type-fewer-paths-with-brace-expansion.md)
|
|
||||||
- [Undo Changes Made To Current Terminal Prompt](unix/undo-changes-made-to-current-terminal-prompt.md)
|
- [Undo Changes Made To Current Terminal Prompt](unix/undo-changes-made-to-current-terminal-prompt.md)
|
||||||
- [Undo Some Command Line Editing](unix/undo-some-command-line-editing.md)
|
- [Undo Some Command Line Editing](unix/undo-some-command-line-editing.md)
|
||||||
- [Unrestrict Where ripgrep Searches](unix/unrestrict-where-ripgrep-searches.md)
|
- [Unrestrict Where ripgrep Searches](unix/unrestrict-where-ripgrep-searches.md)
|
||||||
@@ -1748,7 +1728,6 @@ _1531 TILs and counting..._
|
|||||||
- [Open An Integrated Terminal Window](vscode/open-an-integrated-terminal-window.md)
|
- [Open An Integrated Terminal Window](vscode/open-an-integrated-terminal-window.md)
|
||||||
- [Pop Open The Quick Fix Window](vscode/pop-open-the-quick-fix-window.md)
|
- [Pop Open The Quick Fix Window](vscode/pop-open-the-quick-fix-window.md)
|
||||||
- [Step Through Project-Wide Search Results](vscode/step-through-project-wide-search-results.md)
|
- [Step Through Project-Wide Search Results](vscode/step-through-project-wide-search-results.md)
|
||||||
- [Synchronize Vim Clipboard With System Clipboard](vscode/synchronize-vim-clipboard-with-system-clipboard.md)
|
|
||||||
- [Toggle Between Terminals](vscode/toggle-between-terminals.md)
|
- [Toggle Between Terminals](vscode/toggle-between-terminals.md)
|
||||||
- [Turn Off Display Of Tabs For Files](vscode/turn-off-display-of-tabs-for-files.md)
|
- [Turn Off Display Of Tabs For Files](vscode/turn-off-display-of-tabs-for-files.md)
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
# Get Latest Commit Timestamp For A File
|
|
||||||
|
|
||||||
The `git log` command can tell you all the commits that touched a file. That
|
|
||||||
can be narrowed down to the latest commit for that file with the `-1` flag. The
|
|
||||||
commit that it reports can then be further formatted to with the `--format`
|
|
||||||
flag.
|
|
||||||
|
|
||||||
The `%ai` format pattern gives the date the commit was authored in an ISO
|
|
||||||
8601-like format. The `%aI` (capital `I`) gives the date the commit was
|
|
||||||
authored strictly in the ISO 8601 format.
|
|
||||||
|
|
||||||
Here are examples of both side by side:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
❯ git log -1 --format=%ai -- README.md
|
|
||||||
2024-10-15 13:59:09 -0500
|
|
||||||
|
|
||||||
❯ git log -1 --format=%aI -- README.md
|
|
||||||
2024-10-15T13:59:09-05:00
|
|
||||||
```
|
|
||||||
|
|
||||||
I made use of this in a script where I needed to get an idea of when various
|
|
||||||
files were most recently modified.
|
|
||||||
|
|
||||||
See `man git-log` and the `PRETTY FORMATS` section for more details.
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
# Combine Two Slices
|
|
||||||
|
|
||||||
The `append` function can be used to create a new slice with the contents of
|
|
||||||
the given slice and one or more items added to the end.
|
|
||||||
|
|
||||||
We can add one or more items like so:
|
|
||||||
|
|
||||||
```go
|
|
||||||
s1 := []int{1, 2, 3, 4}
|
|
||||||
s2 := append(s1, 5)
|
|
||||||
s3 := append(s2, 6, 7, 8)
|
|
||||||
|
|
||||||
fmt.Println(s1) //=> [1 2 3 4]
|
|
||||||
fmt.Println(s2) //=> [1 2 3 4 5]
|
|
||||||
fmt.Println(s3) //=> [1 2 3 4 5 6 7 8]
|
|
||||||
```
|
|
||||||
|
|
||||||
But what if we have a second slice instead of individual items? We could import
|
|
||||||
`slices` and use its `Concat` function. Or we can stick with `append` and
|
|
||||||
unpack that slice as a series of arguments into the second part of `append`
|
|
||||||
using `slice...`.
|
|
||||||
|
|
||||||
```go
|
|
||||||
s4 := append(s2, s1...)
|
|
||||||
fmt.Println(s4) //=> [1 2 3 4 5 1 2 3 4]
|
|
||||||
```
|
|
||||||
|
|
||||||
Here is the full example:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
s1 := []int{1, 2, 3, 4}
|
|
||||||
s2 := append(s1, 5)
|
|
||||||
s3 := append(s2, 6, 7, 8)
|
|
||||||
|
|
||||||
fmt.Println(s1)
|
|
||||||
fmt.Println(s2)
|
|
||||||
fmt.Println(s3)
|
|
||||||
|
|
||||||
s4 := append(s2, s1...)
|
|
||||||
fmt.Println(s4)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
[source](https://pkg.go.dev/builtin#append)
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
# Parse A String Into Individual Fields
|
|
||||||
|
|
||||||
Let's say you're reading in data from a file or otherwise dealing with an
|
|
||||||
arbitrary string of data. If that string has a series of values separated by
|
|
||||||
whitespace, you can parse it into individual fields with
|
|
||||||
[`strings.Fields`](https://pkg.go.dev/strings#Fields).
|
|
||||||
|
|
||||||
```go
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
data := "3 5 2 6 7 1 9"
|
|
||||||
fields := strings.Fields(data)
|
|
||||||
|
|
||||||
fmt.Printf("Fields: %v", fields)
|
|
||||||
// [3 5 2 6 7 1 9]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Here is another example where we can see that `strings.Fields` deals with
|
|
||||||
multiple whitespace and surrounding whitespace:
|
|
||||||
|
|
||||||
```go
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
data := " go java c++ rust "
|
|
||||||
fields := strings.Fields(data)
|
|
||||||
|
|
||||||
fmt.Printf("%v", fields)
|
|
||||||
// [go java c++ rust]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
# Parse Flags From CLI Arguments
|
|
||||||
|
|
||||||
Though we can grab the arguments to a Go program from `os.Args`, it requires
|
|
||||||
some manual parsing. With the built-in `flag` package, we can declare specific
|
|
||||||
flags our program accepts, by type. When we parse them, they will be separated
|
|
||||||
out from the rest of the positional arguments.
|
|
||||||
|
|
||||||
Here is an example of the program that accepts a boolean `debug` flag. This
|
|
||||||
will work with either `-debug` or `--debug`.
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
var debug bool
|
|
||||||
flag.BoolVar(&debug, "debug", false, "turns on debug mode, extra logging")
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
positionalArgs := flag.Args()
|
|
||||||
|
|
||||||
if len(positionalArgs) < 1 {
|
|
||||||
fmt.Println("Please specify which part to run: 1 or 2")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if debug {
|
|
||||||
fmt.Println("We are in debug mode...")
|
|
||||||
fmt.Println("Received the following argument:", positionalArgs[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
We can run the program in debug mode like so:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ go run . --debug 123
|
|
||||||
We are in debug mode...
|
|
||||||
Received the following argument: 123
|
|
||||||
```
|
|
||||||
|
|
||||||
We can also take advantage of the `help` flag that we get for free:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ go run . --help
|
|
||||||
Usage of /var/folders/62/lx9pcjbs1zbd83zg6twwym2r0000gn/T/go-build3212087168/b001/exe/test:
|
|
||||||
-debug
|
|
||||||
turns on debug mode, extra logging
|
|
||||||
```
|
|
||||||
|
|
||||||
Note: any recognized flags need to come before any of the position arguments.
|
|
||||||
The `debug` flag won't be picked up if we run the program like this:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ go run . 123 --debug
|
|
||||||
```
|
|
||||||
|
|
||||||
[source](https://pkg.go.dev/flag)
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
# Verify Site Ownership With DNS Record
|
|
||||||
|
|
||||||
To run your site through Google Search Console and get detailed reports, you
|
|
||||||
need to verify that you own the site. There are several manual ways of doing
|
|
||||||
this that involve sticking a value unique to your URL in a file or header tag.
|
|
||||||
There is a better way though.
|
|
||||||
|
|
||||||
By adding a TXT DNS record wherever you domain's DNS is managed, you can prove
|
|
||||||
to Google that you own the domain. That verification applies to all paths and
|
|
||||||
subdomains of that domain.
|
|
||||||
|
|
||||||
Some providers like Cloudflare have a mostly-automated process for this that
|
|
||||||
Google can hook into as long as you grant permission via OAuth.
|
|
||||||
|
|
||||||
You can also manually create the TXT record if necessary.
|
|
||||||
|
|
||||||
Either way, it will look something like:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ dig -t TXT visualmode.dev
|
|
||||||
|
|
||||||
;; ANSWER SECTION:
|
|
||||||
visualmode.dev. 377 IN TXT "google-site-verification=MBZ2S2fhnh2gHRxFniRrYW-O6mdyimJDRFj-f
|
|
||||||
vblwtk"
|
|
||||||
```
|
|
||||||
|
|
||||||
More details are provided in the [Google Search Console
|
|
||||||
docs](https://support.google.com/webmasters/answer/9008080?hl=en#domain_name_verification).
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
# Colocate jj And git Directories For Project
|
|
||||||
|
|
||||||
When doing a standard clone of a git repository with `jj`, you'll get a copy of
|
|
||||||
the project with a `.jj` directory containing the version control information.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ jj git clone git@github.com:jbranchaud/my-repo
|
|
||||||
Fetching into new repo in "/path/of/local/repo"
|
|
||||||
...
|
|
||||||
|
|
||||||
$ exa --tree --all -L 1
|
|
||||||
.
|
|
||||||
├── .gitignore
|
|
||||||
├── .jj
|
|
||||||
├── Cargo.lock
|
|
||||||
├── Cargo.toml
|
|
||||||
└── src
|
|
||||||
```
|
|
||||||
|
|
||||||
This is fine if I'm completely familiar with using
|
|
||||||
[jujutsu](https://martinvonz.github.io/jj/latest/). However, if I'm coming from
|
|
||||||
`git` and still learning, then it would be nice to be able to fallback to
|
|
||||||
familiar `git` commands when needed.
|
|
||||||
|
|
||||||
But without a `.git` directory, I get this:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ git log
|
|
||||||
fatal: not a git repository (or any of the parent directories): .git
|
|
||||||
```
|
|
||||||
|
|
||||||
When cloning a git repo with `jj`, I can instruct it to _colocate_ which means
|
|
||||||
that it will create both the `.jj` and the `.git` data directories in the
|
|
||||||
project.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ jj git clone --colocate git@github.com:jbranchaud/my-repo
|
|
||||||
Fetching into new repo in "/path/of/local/repo"
|
|
||||||
...
|
|
||||||
|
|
||||||
$ exa --tree --all -L 1
|
|
||||||
.
|
|
||||||
├── .git
|
|
||||||
├── .gitignore
|
|
||||||
├── .jj
|
|
||||||
├── Cargo.lock
|
|
||||||
├── Cargo.toml
|
|
||||||
└── src
|
|
||||||
```
|
|
||||||
|
|
||||||
Now I can run `jj` commands or `git` commands:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ git log
|
|
||||||
commit 0c72abbb83657096677f9a3d5ddc7bce20839165 (HEAD, origin/trunk, trunk)
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
[source](https://martinvonz.github.io/jj/latest/git-compatibility/#co-located-jujutsugit-repos)
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
# Find System-wide Config File For User
|
|
||||||
|
|
||||||
The `jj` CLI can be configured in a couple different places. When I recently
|
|
||||||
ran a `jj config` command, I was curious where specifically it was getting set.
|
|
||||||
Those changes didn't appear in the repo's config (`./.jj/repo/config.toml`).
|
|
||||||
That makes sense since it would only apply to that repo. So, where is the
|
|
||||||
system-wide config file?
|
|
||||||
|
|
||||||
The following commond shows where on your machine it is located.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ jj config path --user
|
|
||||||
/Users/jbranchaud/Library/Application Support/jj/config.toml
|
|
||||||
```
|
|
||||||
|
|
||||||
Now, the next time I set a config like this:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ jj config set --user ui.paginate never
|
|
||||||
```
|
|
||||||
|
|
||||||
or want to check what other config options are set to, I can visit that path
|
|
||||||
and take a look.
|
|
||||||
|
|
||||||
[source](https://github.com/martinvonz/jj/blob/main/docs/config.md)
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
# Write System Clipboard To A File
|
|
||||||
|
|
||||||
MacOS has two CLI utilities `pbcopy` and `pbpaste` which, respectively, copy
|
|
||||||
_to_ and paste _from_ the system clipboard via the CLI.
|
|
||||||
|
|
||||||
Let's say I've just copied a large block of text from somewhere onto my system
|
|
||||||
clipboard. I now want to paste that into a new file. Instead of creating a new
|
|
||||||
file, opening it up in my preferred editor, pasting all that text, and saving
|
|
||||||
the file, I can run one small command from the CLI.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ pbpaste > data.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
This redirects the contents of `pbpaste` (which is the system clipboard) into
|
|
||||||
the file `data.txt`. If that file doesn't already exist, then it will be
|
|
||||||
created before the data is written to it.
|
|
||||||
|
|
||||||
See `man pbpaste` for more details.
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
# Organize Pages In Route Groups
|
|
||||||
|
|
||||||
With the Next.js App Router we can organize pages without affecting the URL
|
|
||||||
path structure by nesting those directories and pages within a _Route Group_. A
|
|
||||||
Route Group is directory where the name is surrounded by parentheses, e.g.
|
|
||||||
`/(symbols)`.
|
|
||||||
|
|
||||||
For instance, in my [Ruby Operator
|
|
||||||
Lookup](https://www.visualmode.dev/ruby-operators) project, I have the
|
|
||||||
following structure:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ exa --true src/app/ruby-operators
|
|
||||||
|
|
||||||
src/app/ruby-operators
|
|
||||||
├── (symbols)
|
|
||||||
│ ├── ampersand
|
|
||||||
│ │ └── page.mdx
|
|
||||||
│ ├── arbitrary-keyword-arguments
|
|
||||||
│ │ └── page.mdx
|
|
||||||
│ ├── asterisk
|
|
||||||
│ │ └── page.mdx
|
|
||||||
│ ├── at-symbol
|
|
||||||
│ │ └── page.mdx
|
|
||||||
│ ├── backtick
|
|
||||||
│ │ └── page.mdx
|
|
||||||
│ ├── ...
|
|
||||||
│ └── underscore
|
|
||||||
│ └── page.mdx
|
|
||||||
├── client-layout.tsx
|
|
||||||
├── layout.tsx
|
|
||||||
├── page.tsx
|
|
||||||
└── wrapper.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
I'm able to organize all the different symbols and operators under a separate
|
|
||||||
directory `/(symbol)/`. That makes development easier. However, the end result
|
|
||||||
routing still has each symbol located directly under `/ruby-operators/`, e.g.
|
|
||||||
`/ruby-operators/ampersand`.
|
|
||||||
|
|
||||||
[source](https://nextjs.org/docs/app/getting-started/project-structure#route-groups)
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
# Fetch Specific Number Of Results
|
|
||||||
|
|
||||||
If you pull up just about any intro to PostgreSQL (or even SQL), one of the
|
|
||||||
first things they are going to teach you is the `limit` clause. This is taught
|
|
||||||
as _the_ way for limiting the result set to a specific number of rows.
|
|
||||||
|
|
||||||
```sql
|
|
||||||
> select title from books limit 4;
|
|
||||||
+-----------------------+
|
|
||||||
| title |
|
|
||||||
|-----------------------|
|
|
||||||
| The Secret History |
|
|
||||||
| A Gentleman in Moscow |
|
|
||||||
| Exhalation: Stores |
|
|
||||||
| Annihilation |
|
|
||||||
+-----------------------+
|
|
||||||
SELECT 4
|
|
||||||
```
|
|
||||||
|
|
||||||
You might be as surprised as I was to learn that `limit` is not part of the SQL
|
|
||||||
standard. It is extremely common for this use case, but the SQL standard
|
|
||||||
defines `fetch first N rows only` as the way to fetch a specific number of
|
|
||||||
rows. As we can see, [it works identically to `limit
|
|
||||||
N`](https://www.postgresql.org/docs/current/sql-select.html#SQL-LIMIT).
|
|
||||||
|
|
||||||
```sql
|
|
||||||
> select title from books fetch first 4 rows only;
|
|
||||||
+-----------------------+
|
|
||||||
| title |
|
|
||||||
|-----------------------|
|
|
||||||
| The Secret History |
|
|
||||||
| A Gentleman in Moscow |
|
|
||||||
| Exhalation: Stores |
|
|
||||||
| Annihilation |
|
|
||||||
+-----------------------+
|
|
||||||
SELECT 4
|
|
||||||
```
|
|
||||||
|
|
||||||
The `rows` and `row` keywords are interchangeable which makes statements more
|
|
||||||
readable if, for instance, you're doing `... fetch first 1 row only`.
|
|
||||||
|
|
||||||
[source](https://www.cybertec-postgresql.com/en/postgresql-limit-vs-fetch-first-rows-with-ties/)
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
# Output Explain Query Plan In Different Formats
|
|
||||||
|
|
||||||
The output of an [`explain` (or `explain analyze`) query
|
|
||||||
plan](https://www.postgresql.org/docs/current/sql-explain.html) for a given
|
|
||||||
query defaults to a `TEXT` format that is meant to be read by a person.
|
|
||||||
|
|
||||||
```sql
|
|
||||||
> explain (analyze) select title from books where created_at > now() - '1 year'::interval;
|
|
||||||
QUERY PLAN
|
|
||||||
-------------------------------------------------------------------------------------------------
|
|
||||||
Seq Scan on books (cost=0.00..1.28 rows=5 width=32) (actual time=0.011..0.017 rows=22 loops=1)
|
|
||||||
Filter: (created_at > (now() - '1 year'::interval))
|
|
||||||
Planning Time: 0.052 ms
|
|
||||||
Execution Time: 0.027 ms
|
|
||||||
(4 rows)
|
|
||||||
```
|
|
||||||
|
|
||||||
If we instead want the query plan in a standardized format that is parseable
|
|
||||||
and readable by a program, we can specify an alternate format like `JSON`,
|
|
||||||
`YAML`, or `XML`.
|
|
||||||
|
|
||||||
Here is the same plan with `format json`:
|
|
||||||
|
|
||||||
```sql
|
|
||||||
> explain (analyze, format json) select title from books where created_at > now() - '1 year'::interval;
|
|
||||||
QUERY PLAN
|
|
||||||
----------------------------------------------------------------
|
|
||||||
[ +
|
|
||||||
{ +
|
|
||||||
"Plan": { +
|
|
||||||
"Node Type": "Seq Scan", +
|
|
||||||
"Parallel Aware": false, +
|
|
||||||
"Async Capable": false, +
|
|
||||||
"Relation Name": "books", +
|
|
||||||
"Alias": "books", +
|
|
||||||
"Startup Cost": 0.00, +
|
|
||||||
"Total Cost": 1.28, +
|
|
||||||
"Plan Rows": 5, +
|
|
||||||
"Plan Width": 32, +
|
|
||||||
"Actual Startup Time": 0.008, +
|
|
||||||
"Actual Total Time": 0.014, +
|
|
||||||
"Actual Rows": 22, +
|
|
||||||
"Actual Loops": 1, +
|
|
||||||
"Filter": "(created_at > (now() - '1 year'::interval))",+
|
|
||||||
"Rows Removed by Filter": 0 +
|
|
||||||
}, +
|
|
||||||
"Planning Time": 0.050, +
|
|
||||||
"Triggers": [ +
|
|
||||||
], +
|
|
||||||
"Execution Time": 0.023 +
|
|
||||||
} +
|
|
||||||
]
|
|
||||||
(1 row)
|
|
||||||
```
|
|
||||||
|
|
||||||
I present all four formats for a complex query plan [in this
|
|
||||||
Gist](https://gist.github.com/jbranchaud/731b1a68f5cc70c4f7a9e1f5ef570836).
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
# Block Syntaxes Have Different Precedence
|
|
||||||
|
|
||||||
There are two syntaxes for defining a block in Ruby. The semantically shorthand
|
|
||||||
syntax uses the curly braces (`{}`). The semantically multi-line syntax uses
|
|
||||||
`do` and `end`. For nearly all intents and purposes they are interchangable.
|
|
||||||
|
|
||||||
It is, however, worth noting that the `do`/`end` version has a lower precedence
|
|
||||||
than the already low precedence of `{}`. That said, you have to write some
|
|
||||||
weird code for this to become an issue.
|
|
||||||
|
|
||||||
Let's say we have two methods, `method_one` and `method_two`. They are both
|
|
||||||
called on the same line like below and then followed by a block argument. Which
|
|
||||||
method receives the block argument?
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
method_one method_two { |n|
|
|
||||||
puts "Executing a block: #{n}"
|
|
||||||
}
|
|
||||||
|
|
||||||
method_one method_two do |n|
|
|
||||||
puts "Executing a block: #{n}"
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
In the first case, with the curly braces, `method_two` receives the block as an
|
|
||||||
argument. In the second case, with the `do`/`end`, `method_one` receives the
|
|
||||||
block as an argument.
|
|
||||||
|
|
||||||
[source](http://localhost:3131/ruby-operators/curly-braces#block-shorthand)
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
# Gather Positional Arguments In Method Definition
|
|
||||||
|
|
||||||
The `*` symbol can be used in Ruby in a method definition to gather up an
|
|
||||||
arbitrary number of positional arguments.
|
|
||||||
|
|
||||||
For instance, we can gather all positional arguments with this method
|
|
||||||
definition:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
def gather_all(*args)
|
|
||||||
puts args
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
Or we can isolate the first positional arg and then gather the rest:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
def first_and_rest(first, *rest)
|
|
||||||
puts "First: #{first}, Rest: #{rest}"
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
We can even do something a bit more interesting like isolating the first and
|
|
||||||
last arguments while gathering up everything else in the middle:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
def pop_parens(left, *middle, right)
|
|
||||||
if left != '(' || right != ')'
|
|
||||||
raise "Uh oh!"
|
|
||||||
else
|
|
||||||
if middle.size == 1
|
|
||||||
puts "Found: #{middle.first}"
|
|
||||||
else
|
|
||||||
pop_parens(*middle)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
Here is what it looks like if we splat some different sets of arguments into
|
|
||||||
that method call:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
> tokens1 = "((((4))))".split('')
|
|
||||||
=> ["(", "(", "(", "(", "4", ")", ")", ")", ")"]
|
|
||||||
> tokens2 = "((4))))".split('')
|
|
||||||
=> ["(", "(", "4", ")", ")", ")", ")"]
|
|
||||||
> pop_parens(*tokens1)
|
|
||||||
Found: 4
|
|
||||||
=> nil
|
|
||||||
> pop_parens(*tokens2)
|
|
||||||
(irb):87:in `pop_parens': Uh oh! (RuntimeError)
|
|
||||||
```
|
|
||||||
|
|
||||||
[source](https://ruby-doc.org/3.3.6/syntax/methods_rdoc.html#label-Array-2FHash+Argument)
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
# Stack Heredocs In A Method Call
|
|
||||||
|
|
||||||
When you put a heredoc directly in a method call as an argument, it is only the
|
|
||||||
opening identifier that goes in the argument list.
|
|
||||||
|
|
||||||
That looks like this:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
execute_in_transaction(<<~SQL)
|
|
||||||
update reading_statuses
|
|
||||||
set status = 'abandoned'
|
|
||||||
where started_at < (now() - '2 years'::interval)
|
|
||||||
and finished_at is null;
|
|
||||||
SQL
|
|
||||||
```
|
|
||||||
|
|
||||||
You might imagine then that we can put multiple heredocs in a method call. That
|
|
||||||
leads to [_stacked
|
|
||||||
heredocs_](https://www.visualmode.dev/ruby-operators/heredoc#stacked-heredocs).
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
execute_in_transaction(<<~SQL1, <<~SQL2, <<~SQL3)
|
|
||||||
update reading_statuses
|
|
||||||
set status = 'abandoned'
|
|
||||||
where started_at < (now() - '2 years'::interval)
|
|
||||||
and finished_at is null;
|
|
||||||
SQL1
|
|
||||||
insert into activity_log (name, description)
|
|
||||||
values ('abandon_books', 'Mark unread books as abandoned');
|
|
||||||
SQL2
|
|
||||||
delete from background_jobs
|
|
||||||
where id = #{job_id}; -- better to sanitize values like this
|
|
||||||
SQL3
|
|
||||||
```
|
|
||||||
|
|
||||||
Notice we terminate the body of each heredoc with its closing identifier and
|
|
||||||
immediately begin the body of the next one.
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
# List TXT DNS Records For A Domain
|
|
||||||
|
|
||||||
The `dig` command can be used to list specifically the `TXT` DNS records for a
|
|
||||||
domain using the `-t TXT` flag like so:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ dig -t TXT visualmode.dev
|
|
||||||
|
|
||||||
; <<>> DiG 9.10.6 <<>> -t TXT visualmode.dev
|
|
||||||
;; global options: +cmd
|
|
||||||
;; Got answer:
|
|
||||||
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 41226
|
|
||||||
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
|
|
||||||
|
|
||||||
;; OPT PSEUDOSECTION:
|
|
||||||
; EDNS: version: 0, flags:; udp: 4096
|
|
||||||
;; QUESTION SECTION:
|
|
||||||
;visualmode.dev. IN TXT
|
|
||||||
|
|
||||||
;; ANSWER SECTION:
|
|
||||||
visualmode.dev. 377 IN TXT "v=spf1 include:_spf.mx.cloudflare.net ~all"
|
|
||||||
visualmode.dev. 377 IN TXT "google-site-verification=MBZ2S2fhnh2gHRxFniRrYW-O6mdyimJDRFj-f
|
|
||||||
vblwtk"
|
|
||||||
|
|
||||||
;; Query time: 103 msec
|
|
||||||
;; SERVER: fe80::7c4b:26ff:fe85:e164%6#53(fe80::7c4b:26ff:fe85:e164%6)
|
|
||||||
;; WHEN: Tue Dec 03 12:49:38 CST 2024
|
|
||||||
;; MSG SIZE rcvd: 179
|
|
||||||
```
|
|
||||||
|
|
||||||
This is still rather verbose though. With the `+short` option we can pare down
|
|
||||||
the output to the values of any TXT records and nothing else.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ dig -t TXT visualmode.dev +short
|
|
||||||
"v=spf1 include:_spf.mx.cloudflare.net ~all"
|
|
||||||
"google-site-verification=MBZ2S2fhnh2gHRxFniRrYW-O6mdyimJDRFj-fvblwtk"
|
|
||||||
```
|
|
||||||
|
|
||||||
Neat! Now I can see that [my domain is correctly identifying itself with Google
|
|
||||||
Search Console](internet/verify-site-ownership-with-dns-record.md).
|
|
||||||
|
|
||||||
[source](https://serverfault.com/a/148724)
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
# Type Fewer Paths With Brace Expansion
|
|
||||||
|
|
||||||
Bash has a feature called _brace expansion_ that allows us to do a kind of
|
|
||||||
shorthand when writing out file paths. We can specify multiple variants
|
|
||||||
comma-separated between curly braces and they'll each be expanded into separate
|
|
||||||
arguments.
|
|
||||||
|
|
||||||
It's easier to understand this by seeing it. If we type the following (don't
|
|
||||||
hit `Enter` yet):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ mkdir src/{one,two,three}
|
|
||||||
```
|
|
||||||
|
|
||||||
And then hit _Tab_:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ mkdir src/one src/two src/three
|
|
||||||
```
|
|
||||||
|
|
||||||
Bash uses the portion in braces to expand into separate arguments. The part
|
|
||||||
outside the braces gets reused for each. That's where we get some savings from
|
|
||||||
typing out the same path each time.
|
|
||||||
|
|
||||||
Here is another example where we use `mv` to rename a file deeply nested in our
|
|
||||||
project:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ mv projects/project1/src/app/utils/{names,constants}.js
|
|
||||||
```
|
|
||||||
|
|
||||||
We don't even have to _Tab_ it out. We can hit _Enter_ directly and `mv` gets
|
|
||||||
both arguments.
|
|
||||||
|
|
||||||
Similarly, how about we change the extension of our renamed file:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ mv projects/project1/src/app/utils/constants.{js,ts}
|
|
||||||
```
|
|
||||||
|
|
||||||
I've always found this feature most useful with paths and filenames, but you
|
|
||||||
can do brace expansion with any arguments.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ echo 1{3,1,6,4,9,2,7,5}
|
|
||||||
13 11 16 14 19 12 17 15
|
|
||||||
```
|
|
||||||
|
|
||||||
[source](https://www.gnu.org/software/bash/manual/html_node/Brace-Expansion.html)
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
# Synchronize Vim Clipboard With System Clipboard
|
|
||||||
|
|
||||||
When I use Vim-mode in VSCode, I _yank_ text onto the Vim clipboard by visually
|
|
||||||
selecting some text and hitting `y`. Then I can move the cursor somewhere else
|
|
||||||
in the file (or another file in VSCode) and _paste_ it by hitting `p`.
|
|
||||||
|
|
||||||
But what if I want the thing I yanked from a file to be pasted into another
|
|
||||||
program, like Chrome? Or if I've copied some text from another program and I
|
|
||||||
want to paste it into a file in VSCode?
|
|
||||||
|
|
||||||
This cross-program copy and pasting is what the _system clipboard_ on your
|
|
||||||
operating system is for. By default, the Vim clipboard is separate from the
|
|
||||||
system clipboard. I personally prefer for them to be one and the same.
|
|
||||||
|
|
||||||
To achieve this, I added the following line to my VSCode config in
|
|
||||||
`settings.json`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"vim.useSystemClipboard": true
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
It takes a little getting used to having them integrated, but I've done it for
|
|
||||||
so long that it is muscle memory. It's hard to not have them integrated now.
|
|
||||||
It's even better when I have a clipboard history tool like Raycast available
|
|
||||||
for accessing past clipboard values.
|
|
||||||
Reference in New Issue
Block a user