Changed . token to _dot

This change allows the dotfiles to work with chezmoi (e.g: on windows)
and improves grepability with neovim/telescope
This commit is contained in:
2024-11-07 13:52:17 +00:00
parent 83b02bd753
commit 896af887ca
2351 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
# Helpers
Commands to make your life easier.
## How to use
Execute following snippet at any time to be able to run the commands in the scripts:
```zsh
source './path/to/script.nu'
```
With `./path/to/...` being the path to the script you want to utilize.

View File

@@ -0,0 +1,59 @@
# todo
These two commands can be used to make simple todo-lists that
are printed to the screen at terminal startup. They are currently
only implemented for [alacritty], for other terminals consult
your terminal documentation or find a work around
## SETUP:
- Create a file called `todo.nu` and place the `printer` command,
calling it at the end.
- In your `alacritty.yml`, add this
```yml
shell:
program: /bin/nu # path to your `nu` executable
args:
- -e "nu /path/to/todo.nu"
```
- Create an empty file called `todo.txt`
- *Recommended*
In your `env.nu`, add an environment variable pointing to the `todo.txt`. Example:
```nu
$env.TODO = ($nu.config-path | path dirname | path join 'scripts/todo.txt')
```
- Source the `todo` command in your `config.nu`. Example
```nu
source './scripts/todo.nu'
```
## USAGE:
- On terminal startup, the printer command is run by `nu` and the
list is printed to the screen. If the list is empty, a message
indicating so is printed.
- To add items,
```nu
todo -a "finish writing docs"
```
- To remove an item
```nu
# this prints the list(numbered)
todo
# the index of the item you want to remove
# (starts at 1 but can be changed to start at 0)
todo -r 4
```
- To clear the whole list
```nu
todo -c
```
- To manually edit the list
```
# this opens an editor with the file opened
todo -e
```
[alacritty]: github.com/alacritty/alacritty

View File

@@ -0,0 +1,19 @@
let appID = "" #YOUR APP_ID
#Fetch simple answer from WolframAlpha API
def wolfram [...query #Your query
] {
let query_string = ($query | str join " ")
let result = (http get ("https://api.wolframalpha.com/v1/result?" + ([[appid i]; [$appID $query_string]] | url build-query)))
$result + ""
}
#Fetch image with full answer from WolframAlpha API
def wolframimg [...query #Your query
] {
let query_string = ($query | str join " ")
let filename = ($query_string + ".png")
let link = ("https://api.wolframalpha.com/v1/simple?" + ([[appid i]; [$appID $query_string]] | url build-query) + "&background=F5F5F5&fontsize=20")
http get $link | save $filename
echo ("Query result saved in file: " + $filename)
}

View File

@@ -0,0 +1,19 @@
# cool oneliners
Capturing oneliners to and from the nushell community.
Consider these a living library.
Or an ongoing story of how people actually use `nu`.
## Naming and script requirements
- the filename should be an abbreviation of the general topic of the script
For example:
```sh
git_batch_extract.nu
```
- the script should have two lines
- the first line should be a comment describing the script's purpose
- the second line should be the cool oneliner

View File

@@ -0,0 +1,6 @@
[
["cero", "zero"],
["uno", "one"],
["dos", "two"],
["tres", "three"]
]

View File

@@ -0,0 +1,12 @@
def "cargo search" [ query: string, --limit=10] {
^cargo search $query --limit $limit
| lines
| each {
|line| if ($line | str contains "#") {
$line | parse --regex '(?P<name>.+) = "(?P<version>.+)" +# (?P<description>.+)'
} else {
$line | parse --regex '(?P<name>.+) = "(?P<version>.+)"'
}
}
| flatten
}

View File

@@ -0,0 +1,48 @@
#!/usr/bin/nu
# I actually use it as a part of my startup, so I am not really sure how to pack it, yet I wouldd like to contribute
#-------------------------------------------------------------------------------------------------------------------------------
#
# How to use?
#-------------------------------------------------
#1) Add desired paths to the cdpath variable
#2) Use in your shell: $c [directory]
#2.5) You *have to* use an argument. If you wish to simply $cd, use $cd command.
#3) If the path exists, you will cd into the first match found (the command is iterating over the list in the correct order,
# i.e. first element is being iterated overin the first place)
#3.5) But if path does not exist, you will receive a proper echo.
#-----------------------------------------------------------------------------------------------------------------------------------
#
#Written by skelly37
#------------------------
# startup = [
# "let cdpath = [. /place/your ~/cdpath/here ]",
# "def c [dir] { let wd = (pwd); for element in $cdpath {if (pwd) == $wd {cd $element; for directory in (ls -a | select name type | each { if $it.type == Dir {echo $it.name} {} } ) {if $dir == $directory {cd $dir} {}}; if (pwd) == $element {cd $wd} {}} {}}; if (pwd) == $wd {cd $wd; echo \"No such path!\"} {}}",
# ]
#
export def --env c [dir] {
let CD_PATH = [. ($env.NU_PLUGIN_DIRS | get 0) $nu.default-config-dir ]
let wd = (pwd);
for element in $CD_PATH {
let element = ($element | path expand)
if (pwd) == $wd {
cd $element;
for directory in (ls -a | where type == dir | get name) {
if $dir == $directory {
cd $dir
break
}
};
if (pwd) == $element {
cd $wd
}
}
};
if (pwd) == $wd {
cd $wd
print "No such path!"
}
}

View File

@@ -0,0 +1,8 @@
### This clears out your screen buffer on a default mac terminal
### currently there is no way to do that in nushell
def cls [] {
ansi cls
ansi clsb
ansi home
}

View File

@@ -0,0 +1,21 @@
# Function querying free online English dictionary API for definition of given word(s)
def dict [...word #word(s) to query the dictionary API but they have to make sense together like "martial law", not "cats dogs"
] {
let query = ($word | str join %20)
let link = ('https://api.dictionaryapi.dev/api/v2/entries/en/' + ($query|str replace ' ' '%20'))
let output = (http get $link | rename word)
let w = ($output.word | first)
if $w == "No Definitions Found" {
echo $output.word
} else {
echo $output
| get meanings
| flatten
| select partOfSpeech definitions
| flatten
| flatten
| reject "synonyms"
| reject "antonyms"
}
}

View File

@@ -0,0 +1,11 @@
# Combine two files into one
def create_files [] {
[0,1,2,3] | range 0..3 | save a.json
[4,5,6,7] | range 0..3 | save b.json
}
create_files
echo (open a.json) (open b.json) | save c.json
open c.json | flatten
rm a.json b.json c.json

View File

@@ -0,0 +1,7 @@
# rename any SQL files in current directory from hello_file.sql to hello-file.sql
ls ./*.sql | each { |f|
let ext = (echo $f.name | path parse | get extension);
let cur_stem = (echo $f.name | path parse | get stem);
let new_name = (build-string (echo $cur_stem | str kebab-case) "." $ext)
mv $f.name $new_name
}

View File

@@ -0,0 +1,2 @@
## show directory sizes in current directory starting from the largest
ls -d|where type == dir|sort-by size|reverse|format filesize GB size

View File

@@ -0,0 +1,12 @@
# Search terms in the specified files and/or folders based on the glob pattern provided.
def "find in" [
glob: glob, # the glob expression
...rest: any # terms to search
]: nothing -> table<path: string, line: int, data: string> {
glob $glob
| par-each {|e|
open $e | lines | enumerate | rename line data |
find -c [data] ...$rest |
each {|match| {path: ($e | path relative-to $env.PWD), ...$match}}
} | flatten
}

View File

@@ -0,0 +1,2 @@
# gently try to delete merged branches, excluding the checked out one
git branch --merged | lines | where $it !~ '\*' | str trim | where $it != 'master' and $it != 'main' | each { |it| git branch -d $it }

View File

@@ -0,0 +1,2 @@
# Ingest JavaScript Map JSON into nu then to markdown
open assets/js_map.json | each { echo [[Español English]; [ $in.0 $in.1]] } | flatten | to md

View File

@@ -0,0 +1,2 @@
# Increment the minor version for any package.json in the current directory with `nu_plugin_inc` (eigenrick — 08/16/2020)
ls -f */package.json | each {|it| open $it.name | inc version --minor | to json --indent 2 | save --raw --force $it.name }

View File

@@ -0,0 +1 @@
open ~/.config/fish/fish_history | from yaml | get cmd | find --regex '^git .*' | split column ' ' command subcommand

View File

@@ -0,0 +1,4 @@
# Print working directory but abbreviates the home dir as ~
def pwd-short [] {
$env.PWD | str replace $nu.home-path '~'
}

View File

@@ -0,0 +1,2 @@
# Makes an http request to a URL and gets the "value" field off the JSON response.
(http get https://api.chucknorris.io/jokes/random).value

View File

@@ -0,0 +1,2 @@
# Evaluates the top 5 most used commands present in `nushell/history.txt`.
if $nu.history-enabled { open $nu.history-path | lines | uniq --count | sort-by --reverse count | first 5 | rename command amount } else { print --stderr "History is disabled!" }

View File

@@ -0,0 +1,6 @@
# Search the WordprocessingML XML Schema definition file for a simple type by name
# You'll need the wml.xsd file.
# To get that file, first download the following zip:
# https://www.ecma-international.org/wp-content/uploads/ECMA-376-Fifth-Edition-Part-1-Fundamentals-And-Markup-Language-Reference.zip
# Then, unzip the contents of OfficeOpenXML-XMLSchema-Strict.zip.
open wml.xsd | from xml | get content | where tag == simpleType | flatten | where name =~ BrType | get content.content.0.attributes

View File

@@ -0,0 +1,42 @@
def "nu core-team" [] {
[
[ 'name', 'tz'];
[ 'andres', 'America/Guayaquil']
[ 'fdncred', 'US/Central']
[ 'gedge', 'US/Eastern']
[ 'jt', 'NZ']
[ 'wycats', 'US/Pacific']
[ 'kubouch', 'Europe/Helsinki']
['elferherrera', 'Europe/London']
]
}
def "date local" [now] {
insert time {|value|
let converted = ($now | date to-timezone $value.tz);
$converted | format date '%c'
}
}
let next_call = ("2021-08-31 15:00:21.290597200 -05:00" | into datetime);
let people = (nu core-team | date local $next_call);
def say [closure] {
$in | each {|person|
do $closure (
$person | update name {|row| $row.name | str capitalize}
)
} | str join (char newline)
}
print ($people | say {|person| $"($person.name)'s timezone is ($person.tz)"})
print ($"
for the next call happening on ($next_call | format date '%c').. in their timezones they would be:
")
print ($people | say {|person| $"($person.name)'s: ($person.time)"})

View File

@@ -0,0 +1,25 @@
let ns = (netstat | lines | skip 1)
let first_batch = ($ns | take until {|it| $it =~ Active } | str join (char nl) | from ssv -m 1)
let second_batch = ($ns |
skip until {|it| $it =~ Active } |
skip 1 |
str join (char nl) |
str replace '\[ \]' "[]" --all |
from ssv -m 1 |
default I-Node "" |
default Path ""
| each {|row| if $row.Type == DGRAM {
$row | update Path { get I-Node } | update I-Node { get State } | update State ""
} else {
$row
}
}
)
print $first_batch
print $second_batch

View File

@@ -0,0 +1,69 @@
#!/usr/bin/env engine-q
#
# ~/.fehbg.nu
#
# This script selects a random image from a directory and sets it as a
# wallpaper. An additional feature is that it draws the path of the image in
# the lower left corner which makes it easier to find the image later.
#
# The procedure is as follows::
# * Select a random image from a directory (recursively entering all
# subdirectories)
# * Print the path of the image on top of the selected image
# * Save the result in a temporary file
# * Set the temporary image as a wallpaper.
#
# !! Requires `WALLPAPER_DIR` and `TMP_DIR` environment variables to be set !!
# (or just hardcode your own paths below)
#
# You can set this script to run on desktop startup, e.g., by adding it to
# xinitrc.
#
# Dependencies;
# * new Nushell engine (https://github.com/nushell/engine-q)
# * feh
# * imagemagick
# Path definitions
let img_dir = $env.WALLPAPER_DIR
let tmp_image = ([ $env.TMP_DIR "wallpaper.jpg" ] | path join)
# Monitor resolution
let resolution_y = 1440
let res_str = ($"x($resolution_y)")
# Position of the caption
let pos_x = 5
let pos_y = (0.995 * $resolution_y | into int)
# Select random item from input
def select-random [] {
shuffle | first
}
# List all images in a directory and all its subdirectories
def list-images [dir: path] {
ls $"($dir)/**/*" |
where type == file |
str downcase name |
where name =~ jpg or name =~ jpeg or name =~ tif or name =~ tiff or name =~ png
}
# Set the caption text (just filename for now)
def caption [img_f: string] {
echo $img_f
}
# Build the argument for the '-draw' command of the 'convert' utility
def draw-str [img_f: string] {
$"text ($pos_x), ($pos_y) (char dq)(caption $img_f)(char dq) "
}
# Select random image
let img_name = (list-images $img_dir | select-random | get name) # TODO: change the env var to $img-dir
# Resize the image to the monitor height, draw the caption and save it
convert -resize $res_str -pointsize 15 -fill 'rgb(255,200,150)' -draw (draw-str $img_name) $img_name $tmp_image
# Set the created image as a background
feh --no-fehbg --bg-max $tmp_image

View File

@@ -0,0 +1,169 @@
# You must set $env.CDPATH, try:
#
# $env.CDPATH = [
# ".",
# "~",
# "~/path/to/repositories",
# ]
#
# The above $env.CDPATH will complete:
# * Entries under the current directory ($env.PWD)
# * Entries in your home directory ($env.HOME)
# * Entries where you check out repositories
# * Children of those entries
#
# This CDPATH implementation also completes absolute paths to help you use `c`
# instead of `cd`
#
# Bugs:
# * `c` does not complete paths that start with "~". This should support both
# directories ("~/.config/nu"), users ("~notme"), and both combined
# ("~notme/.config/nu")
module cdpath {
# $env.CDPATH with unique, expanded, existing paths
def cdpath [] {
$env.CDPATH
| path expand
| uniq
| filter {|| $in | path exists }
}
# Children of a path
def children [path: string] {
ls -a $path
| where type == "dir"
| get name
}
# Completion for `c`
#
# `contains` is used instead of `starts-with` to behave similar to fuzzy
# completion behavior.
#
# During completion of a CDPATH entry the description contains the parent
# directory you will complete under. This allows you to tell which entry in
# your CDPATH your are completing to if you have the same directory under
# multiple entries.
def complete [context: string] {
let context_dir = $context | parse "c {context_dir}" | get context_dir | first
let path = $context_dir | path split
let no_trailing_slash = not ($context_dir | str ends-with "/")
# completion with no context
if ( $path | is-empty ) {
complete_from_cdpath
# Complete an entry in CDPATH
#
# This appends a / to allow continuation to the last step
} else if $no_trailing_slash and (1 == ( $path | length )) {
let first = $path | first
complete_from_cdpath
| filter {|| $in.value | str contains $first }
| upsert value {|| $"($in.value)/" }
# Complete a child of a CDPATH entry
} else {
let prefix = if 1 == ($path | length) {
$path | first
} else {
$path | first (($path | length) - 1) | path join
}
let last = if 1 == ($path | length) {
""
} else {
$path | last
}
let chosen_path = if ( $path | first) == "/" {
if $no_trailing_slash {
$prefix
} else {
$context_dir
}
} else {
cdpath
| each {||
$in | path join $prefix
}
| filter {||
$in | path exists
}
| first
}
children $chosen_path
| filter {||
$in | str contains $last
}
| each {|child|
$"($chosen_path | path join $child)/"
}
}
}
def complete_from_cdpath [] {
cdpath
| each { |path|
children $path
| path basename
| sort
| each { |child| { value: $child, description: $path } }
}
| flatten
| uniq-by value
}
# Change directory with $env.CDPATH
export def --env c [dir = "": string@complete] {
let span = (metadata $dir).span
let default = if $nu.os-info.name == "windows" {
$env.USERPROFILE
} else {
$env.HOME
}
let target_dir = if $dir == "" {
$default
} else if $dir == "-" {
if "OLDPWD" in $env {
$env.OLDPWD
} else {
$default
}
} else {
cdpath
| reduce -f "" { |$it, $acc|
if $acc == "" {
let new_path = ([$it $dir] | path join)
if ($new_path | path exists) {
$new_path
} else {
""
}
} else {
$acc
}
}
}
let target_dir = if $target_dir == "" {
let cdpath = $env.CDPATH | str join ", "
error make {
msg: $"No such child under: ($cdpath)",
label: {
text: "Child directory",
start: $span.start,
end: $span.end,
}
}
} else {
$target_dir
}
cd $target_dir
}
}
use cdpath c

View File

@@ -0,0 +1,8 @@
use std repeat;
# Go up a number of directories
def --env up [
limit = 1: int # The number of directories to go up (default is 1)
] {
cd ("." | repeat ($limit + 1) | str join)
}

View File

@@ -0,0 +1,36 @@
# Note: this requires the latest 0.32.1 or later
#
# usage:
# > source lisp_mode.nu
# > (echo (+ 1 (* 3 2)))
def "+" [x, y] { $x + $y }
def "-" [x, y] { $x - $y }
def "*" [x, y] { $x * $y }
def "/" [x, y] { $x / $y }
def in [x, y] { $x in $y }
def not-in [x, y] { $x not-in $y}
def "<" [x, y] { $x < $y }
def "<=" [x, y] { $x <= $y }
def ">" [x, y] { $x > $y }
def ">=" [x, y] { $x >= $y }
def "==" [x, y] { $x == $y }
def "!=" [x, y] { $x != $y }
def "=~" [x, y] { $x =~ $y }
def "!~" [x, y] { $x !~ $y }
def array [...rest] { echo $rest }

View File

@@ -0,0 +1,54 @@
let TICKS = [(char -u "2581")
(char -u "2582")
(char -u "2583")
(char -u "2584")
(char -u "2585")
(char -u "2586")
(char -u "2587")
(char -u "2588")]
# send an array into spark and get a sparkline out
# let v = [2, 250, 670, 890, 2, 430, 11, 908, 123, 57]
# > spark $v
# ▁▂▆▇▁▄▁█▁▁
# create a small sparkline graph
def spark [v: list] {
let min = ($v | math min)
let max = ($v | math max)
let ratio = (if $max == $min { 1.0 } else { 7.0 / ($max - $min)})
$v | each { |e|
let i = ((($e - $min) * $ratio) | math round)
$"($TICKS | get $i)"
} | str join
}
def assert_eq [num: int, expected: string, input: list] {
let actual = (spark2 $input)
let span = (metadata $expected).span;
if $actual != $expected {
error make {
msg: "Actual != Expected",
label: {
text: $"expected ($expected) but got ($actual)", start: $span.start, end: $span.end
}
}
} else {
print $"Test ($num) (ansi green)passed(ansi reset)"
}
}
def spark2_tests [] {
assert_eq 1 "▁" [1.0]
assert_eq 2 "▁▁" [1.0, 1.0]
assert_eq 3 "▁▁▁▁" [1.0, 1.0, 1.0, 1.0]
assert_eq 4 "▁▅▄▇▄█" [1.0, 1.3, 1.2, 1.4, 1.2, 1.5]
assert_eq 5 "▁█▅" [-1.0, 1.0, 0.0]
assert_eq 6 "▁▂█▅▂" [1.0, 5.0, 22.0, 13.0, 5.0]
assert_eq 7 "▁▂▄▅▃█" [0.0, 30.0, 55.0, 80.0, 33.0, 150.0]
assert_eq 8 "▁▁▁▁▃▁▁▁▂█" [1.0, 2.0, 3.0, 4.0, 100.0, 5.0, 10.0, 20.0, 50.0, 300.0]
assert_eq 9 "▁▄█" [1.0, 50.0, 100.0]
assert_eq 10 "▁▄█" [0.1, 5.0, 10.0]
assert_eq 11 "▁▃█" [2.0, 4.0, 8.0]
# assert_eq 12 "▁█" [2.0, 4.0, 8.0]
}

View File

@@ -0,0 +1,96 @@
# Converts markdown into their equivalent html pages
let $markdown_files = (ls **/*.md)
for $markdown in $markdown_files {
let $contents = (open --raw $markdown.name)
let $content_lines = ($contents | lines)
let $first_line = ($content_lines | first | str trim)
if $first_line == "---" {
let $header = ($content_lines
| skip 1
| take while {|x| ($x | str trim) != "---"}
| str join "\n"
| from yaml)
let $post = ($content_lines
| skip 1
| skip while {|x| ($x | str trim) != "---"}
| skip 1)
print $header
let $html_post = ($post
| each {|line|
if ($line | str starts-with "#") {
let $line = ($line | str replace "^# (.*)$" "<h1>$1</h1>")
let $line = ($line | str replace "^## (.*)$" "<h2>$1</h2>")
let $line = ($line | str replace "^### (.*)$" "<h3>$1</h3>")
$line
} else if $line != "" {
# Otherwise, it's a paragraph
# Convert images
let $line = ($line | str replace --all '!\[(.+)\]\((.+)\)' '<img src="$2" alt="$1"/>')
let $line = ($line | str replace --all 'src="../assets' 'src="assets')
# Convert links
let $line = ($line | str replace --all '\[(.+?)\]\((.+?)\)' '<a href="$2">$1</a>')
# Convert code
let $line = ($line | str replace --all '`(.+?)`' '<code>$1</code>')
$"<p>($line)</p>"
}
})
let $html_post = ($html_post | str join "\n")
let $html_post = $"<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>($header.title)</title>
<style>
img {
max-width: 600px
}
p {
display: block;
margin-block-start: 1em;
margin-block-end: 1em;
margin-inline-start: 0px;
margin-inline-end: 0px;
}
body {
font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;
-webkit-font-smoothing: antialiased;
font-size: 16px;
color: #2c3e50;
line-height: 1.7;
max-width: 740px;
margin: 0 auto;
padding: 2rem 2.5rem;
}
code {
color: #476582;
padding: .25rem .5rem;
margin: 0;
font-size: .85em;
background-color: #eeeeee;
border-radius: 3px;
}
</style>
</head>
<body>
($html_post)
</body>
</html>"
# print $html_post
let $name = ($markdown.name | path parse | update extension "html" | path join)
$html_post | save --raw $name
}
}

View File

@@ -0,0 +1,12 @@
# Branch Protections
### Definition
Do you have hundreds or thousands of GitHub repositories in your organization? Are you tired of manually managing their branch protection rules? Don't! Let nushell do it for you!
### Setup
1. Replace placeholder data in .nu script with your own (or remove the appropriate fields if you don't need to i.e push to repo from action)
1. Create a repo in your organization account to store the github action
1. Push both the attached script and the github action to the repo
### Possible future improvements
* Instead of cron run the script on repository creation event (once org level actions become a thing in GitHub)

View File

@@ -0,0 +1,42 @@
#!/usr/bin/env nu
let protections = {
required_status_checks: {
strict: true
checks: [
{
context: 'YOUR CHECK HERE'
app_id: 'YOUR APP ID HERE'
}
]
}
required_pull_request_reviews: {
dismiss_stale_reviews: true
require_code_owner_reviews: true
bypass_pull_request_allowances: {
apps: [
YOUR APP HERE
]
}
}
restrictions: {
users: []
teams: []
apps: [
YOUR APP HERE
]
}
enforce_admins: true
required_linear_history: true
require_conversation_resolution: true
allow_deletions: false
allow_force_pushes: false
}
gh api $"orgs/($env.OWNER)/repos"
|from json
|select name default_branch
|each {|repo|
echo $"Setting branch restrictions for ($repo.name)"
$protections
|to json
|gh api -X PUT $"repos/($env.OWNER)/($repo.name)/branches/($repo.default_branch)/protection" --input -
}

View File

@@ -0,0 +1,25 @@
---
name: Add branch protections to all repositories
'on':
schedule:
- cron: '0 * * * *'
workflow_dispatch:
defaults:
run:
shell: nu {0}
jobs:
set-branch-restrictions:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Nu
uses: hustcer/setup-nu@main
with:
version: '0.64.0'
- run: ./branch-protections.nu
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OWNER: ${{ github.repository_owner }}

View File

@@ -0,0 +1,9 @@
# Merged Branches
### Definition
Do your developers often forget to delete their branches after merging PRs? Are you tired of manually going into every repository and deleting them? Don't! Let nushell do it for you!
### Setup
1. Create a repo in your organization account to store the github action
1. Push both the attached script and the github action to the repo

View File

@@ -0,0 +1,15 @@
#!/usr/bin/env nu
gh api $"orgs/($env.OWNER)/repos"
|from json
|each {|repo|
gh api $"repos/($env.OWNER)/($repo.name)/pulls?state=closed"
|from json
|if ($in|length) > 0 {
each {|pull|
print $"Removing branch ($pull.head.ref) from repo ($repo.name)"
gh api -X DELETE $"repos/($env.OWNER)/($repo.name)/git/refs/heads/($pull.head.ref)"
}
} else {
print $"Repo ($repo.name) has no branches to delete"
}
}

View File

@@ -0,0 +1,25 @@
---
name: Delete merged branches from all repositories
'on':
schedule:
- cron: '0 * * * *'
workflow_dispatch:
defaults:
run:
shell: nu {0}
jobs:
delete-merged-branches:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Nu
uses: hustcer/setup-nu@main
with:
version: '0.64.0'
- run: ./merged-branches.nu
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OWNER: ${{ github.repository_owner }}

View File

@@ -0,0 +1,13 @@
# Gitlab Scanner
### Definition
I use this script to scan contents of my company's GitLab server. Due to nushell's use of multithreading I'm able to scan around 1k repositories in about 9 seconds
### Setup
1. Generate GitLab Personal Access Token and save it as `GITLAB_TOKEN` environment variable
2. Run the script providing necessary data as arguments (or hardcode them in the script if you don't expect them to change often)
### Possible future improvements
* Multiple files/phrases/branches to search
* Maybe create some stats like how many times a given phrase was found in a repo or file
* Maybe offer an option to replace a phrase and automatically push the updated file or create a merge request

View File

@@ -0,0 +1,39 @@
#!/usr/bin/env nu
let base_url = ""
let page_size = 100
let projects = $"($base_url)/api/v4/projects/"
def call-gitlab [
...args: string
--query: string
] {
http get -H [Authorization $"Bearer ($env.GITLAB_TOKEN)"] $"($projects)($args|str join)?($query)"
}
# Search files on your GitLab server
def main [
--base_url: string # base url of your GitLab instance
--file: string # file (or path to file if in a subfolder) you want to scan
--phrase: string # phrase you want to search for
--branch: string # branch to scan
] {
# /projects endpoint can return up to $page_size items which is why we need multiple calls to retrieve full list
let num_of_pages = ((call-gitlab --query 'page=1&per_page=1&order_by=id&simple=true'|get id.0|into int) / $page_size|math round)
seq 1 $num_of_pages|par-each {|page|
call-gitlab --query $"page=($page)&per_page=($page_size)"|select name id
}
|flatten
|par-each {|repo|
let payload = (call-gitlab $repo.id '/repository/files/' $file --query $"ref=($branch)")
if ($payload|columns|find message|is-empty) {
$payload
|get content
|decode base64
|lines
|find $phrase
|if ($in|length) > 0 {
echo $"($file) in ($repo.name) repo contains ($phrase) phrase"
}
}
}
}

View File

@@ -0,0 +1,4 @@
These nushell scripts are semi-clones of the shell scripts in the same folder. They work with iterm2 on MacOS and maybe other shells.
- divider.nu will print a thick divider line for us with a nushell hook
- imgcat.nu will print a png image to the terminal screen

View File

@@ -0,0 +1,8 @@
def divider [] {
let a = "\e]1337;File=inline=1;width=100%%;height=1;preserveAspectRatio=0"
let b = ":"
# this is just a 1x1 white png image
let c = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAABb2lDQ1BpY2MAACiRdZE7SwNBFIW/RIOikRRaiFgsEkVEISiIpcbCJkiICkZtknWTCNl12U0QsRVsLAQL0cZX4T/QVrBVEARFELHyB/hqJKx3jJAgySyz9+PMnMvMGfDH8rrpNkbAtApOYiqqzScXtKZXAoQIMkBPSnftiXg8Rt3xdY9P1bsh1av+vpqjddlwdfA1C4/qtlMQHheOrRVsxdvCHXoutSx8JDzoyAGFr5WeLvOL4myZPxQ7s4lJ8KueWraK01Ws5xxTuF84bOaL+t951E2ChjU3I7VLZjcuCaaIopGmyAp5CgxJtSSz2r7Ir2+aVfHo8rdZxxFHlpx4B0UtSldDakZ0Q7486yr3/3m6mZHhcvdgFALPnvfeC027UNrxvO9jzyudQMMTXFoV/6rkNPYp+k5FCx9CaBPOrypaeg8utqDz0U45qV+pQaY/k4G3M2hLQvsttCyWs/pb5/QBZjfkiW5g/wD6ZH9o6Qf3IWgGVlxmOAAAAANQTFRF////p8QbyAAAAAlwSFlzAAAPYQAAD2EBqD+naQAAAApJREFUCFtjYAAAAAIAAWJAT2gAAAAASUVORK5CYII='
let d = "\a"
[ $a $b $c $d ] | str join
}

View File

@@ -0,0 +1,9 @@
#!/bin/bash
if [ $# -eq 0 ]; then
echo "Usage: divider file"
exit 1
fi
printf '\033]1337;File=inline=1;width=100%%;height=1;preserveAspectRatio=0'
printf ":"
base64 < "$1"
printf '\a\n'

View File

@@ -0,0 +1,91 @@
# https://iterm2.com/documentation-images.html
def print_osc [] {
if $env.TERM == screen* {
"\ePtmux;\e\e]"
} else {
"\e]"
}
}
def print_st [] {
if $env.TERM == screen* {
"\a\e\\"
} else {
"\a"
}
}
def --env b64_encode [fn] {
open $fn | encode base64
}
def --env b64_decode [fn] {
$fn | decode base64
}
def print_image [
filename # Filename to convey to client
inline # 0 or 1
base64contents # Base64-encoded contents
print_filename # If non-empty, print the filename before outputting the image
] {
let a = (print_osc)
let b = "1337;File="
let c = (if ($filename | length) > 0 {
let b64_enc_data = (b64_encode $filename)
$"name=($b64_enc_data);"
})
let b64_dec_data = (b64_decode $base64contents)
let d = $"size=($b64_dec_data | bytes length)"
let e = $";inline=($inline)"
let f = ":"
let g = $base64contents
let h = print_st
let i = "\n"
let j = (if ($print_filename | length) > 0 {
print -n $filename
})
[ $a $b $c $d $e $f $g $h $i $j ] | str join
}
def error [] {
print "Error: ($env.LAST_EXIT_CODE)"
}
def show_help [] {
print "Usage: imgcat [-p] filename ..."
print " or: cat filename | imgcat"
}
# imgcat.nu shows images in your terminal if your terminal supports it
def imgcat [
--help(-h) # Help/Usage message
--print(-p) # Print filename
--url(-u) # Use a URL
filename # The filename to show
] {
if $help {
show_help
}
let print_filename = (
if $print {
1
}
)
let url_img = (
if $url {
let encoded_image = (b64_encode (http get $url))
print_image $url 1 $encoded_image $print_filename
}
)
if ($filename | path exists) {
print_image $filename 1 (b64_encode $filename) $print_filename
} else {
print $"imgcat: ($filename): No such file or directory"
}
}

View File

@@ -0,0 +1,157 @@
#!/bin/bash
# tmux requires unrecognized OSC sequences to be wrapped with DCS tmux;
# <sequence> ST, and for all ESCs in <sequence> to be replaced with ESC ESC. It
# only accepts ESC backslash for ST. We use TERM instead of TMUX because TERM
# gets passed through ssh.
function print_osc() {
if [[ $TERM == screen* ]]; then
printf "\033Ptmux;\033\033]"
else
printf "\033]"
fi
}
# More of the tmux workaround described above.
function print_st() {
if [[ $TERM == screen* ]]; then
printf "\a\033\\"
else
printf "\a"
fi
}
function load_version() {
if [ -z ${IMGCAT_BASE64_VERSION+x} ]; then
IMGCAT_BASE64_VERSION=$(base64 --version 2>&1)
export IMGCAT_BASE64_VERSION
fi
}
function b64_encode() {
load_version
if [[ $IMGCAT_BASE64_VERSION =~ GNU ]]; then
# Disable line wrap
base64 -w0
else
base64
fi
}
function b64_decode() {
load_version
if [[ $IMGCAT_BASE64_VERSION =~ fourmilab ]]; then
BASE64ARG=-d
elif [[ $IMGCAT_BASE64_VERSION =~ GNU ]]; then
BASE64ARG=-di
else
BASE64ARG=-D
fi
base64 $BASE64ARG
}
# print_image filename inline base64contents print_filename
# filename: Filename to convey to client
# inline: 0 or 1
# base64contents: Base64-encoded contents
# print_filename: If non-empty, print the filename
# before outputting the image
function print_image() {
print_osc
printf '1337;File='
if [[ -n $1 ]]; then
printf "name=%s;" "$(printf "%s" "$1" | b64_encode)"
fi
printf "%s" "$3" | b64_decode | wc -c | awk '{printf "size=%d",$1}'
printf ";inline=%s" "$2"
printf ":"
printf "%s" "$3"
print_st
printf '\n'
if [[ -n $4 ]]; then
echo "$1"
fi
}
function error() {
echo "ERROR: $*" 1>&2
}
function show_help() {
echo "Usage: imgcat [-p] filename ..." 1>&2
echo " or: cat filename | imgcat" 1>&2
}
function check_dependency() {
if ! (builtin command -V "$1" >/dev/null 2>&1); then
echo "imgcat: missing dependency: can't find $1" 1>&2
exit 1
fi
}
## Main
if [ -t 0 ]; then
has_stdin=f
else
has_stdin=t
fi
# Show help if no arguments and no stdin.
if [ $has_stdin = f ] && [ $# -eq 0 ]; then
show_help
exit
fi
check_dependency awk
check_dependency base64
check_dependency wc
# Look for command line flags.
while [ $# -gt 0 ]; do
case "$1" in
-h | --h | --help)
show_help
exit
;;
-p | --p | --print)
print_filename=1
;;
-u | --u | --url)
check_dependency curl
encoded_image=$(curl -s "$2" | b64_encode) || (
error "No such file or url $2"
exit 2
)
has_stdin=f
print_image "$2" 1 "$encoded_image" "$print_filename"
set -- "${@:1:1}" "-u" "${@:3}"
if [ "$#" -eq 2 ]; then
exit
fi
;;
-*)
error "Unknown option flag: $1"
show_help
exit 1
;;
*)
if [ -r "$1" ]; then
has_stdin=f
print_image "$1" 1 "$(b64_encode <"$1")" "$print_filename"
else
error "imgcat: $1: No such file or directory"
exit 2
fi
;;
esac
shift
done
# Read and print stdin
if [ $has_stdin = t ]; then
print_image "" 1 "$(cat | b64_encode)" ""
fi
exit 0

View File

@@ -0,0 +1,63 @@
let dictionary = [
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4',
'5', '6', '7', '8', '9', '+', '/'
]
let padding_char = '='
def base64_encode [input: string] {
mut result = ""
mut counter = 0
mut left_carry = 0
if ($input | is-empty) {
error make {msg: "input is empty"}
}
for char in ($input | split chars) {
let char_bin = ($char | into binary)
let char_int = ($char_bin | into int)
if $counter mod 3 == 0 {
let index = ($char_int bit-shr 2)
$result += ($dictionary | get $index)
$left_carry = ($char_int bit-and 0x3)
} else if $counter mod 3 == 1 {
let index = (($left_carry bit-shl 4) bit-or ($char_int bit-shr 4))
$result += ($dictionary | get $index)
$left_carry = ($char_int bit-and 0xF)
} else if $counter mod 3 == 2 {
mut index = (($left_carry bit-shl 2) bit-or ($char_int bit-shr 6))
$result += ($dictionary | get $index)
$index = ($char_int bit-and 0x3F)
$result += ($dictionary | get $index)
}
$counter += 1
}
if $counter != 0 {
if $counter mod 3 == 1 {
$result += ($dictionary | get ($left_carry bit-shl 4))
$result += $padding_char
$result += $padding_char
} else if $counter mod 3 == 2 {
$result += ($dictionary | get ($left_carry bit-shl 2))
$result += $padding_char
}
}
$result
}
# Test Cases
# base64_encode "nushell", "bnVzaGVsbA=="
# base64_encode "hello", "aGVsbG8="
# base64_encode "world", "d29ybGQ="
# base64_encode "now is the time for all good mean to come to the aid of their country", "bm93IGlzIHRoZSB0aW1lIGZvciBhbGwgZ29vZCBtZWFuIHRvIGNvbWUgdG8gdGhlIGFpZCBvZiB0aGVpciBjb3VudHJ5
# base64_encode "crab", "Y3JhYg=="
# base64_encode "the brown fox jump over the lazy dog!"), "dGhlIGJyb3duIGZveCBqdW1wIG92ZXIgdGhlIGxhenkgZG9nIQ=="
# base64_encode "", error
# base64_encode "a", "YQ=="

View File

@@ -0,0 +1,18 @@
export def main [] {
let builtin = (scope commands | where type == "built-in" | length)
let external = (scope commands | where type == "external" | length)
let custom = (scope commands | where type == "custom" | length)
let keyword = (scope commands | where type == "keyword" | length)
let plugin = (scope commands | where type == "plugin" | length)
let total = (scope commands | length)
[
[command_type count];
[builtin $builtin]
[external $external]
[custom $custom]
[keyword $keyword]
[plugin $plugin]
[total_cmds $total]
]
}

View File

@@ -0,0 +1,513 @@
#copy current dir
def cpwd [] {pwd | tr "\n" " " | sed "s/ //g" | xclip -sel clip}
#update-upgrade system (ubuntu)
def supgrade [] {
echo "updating..."
sudo aptitude update -y
echo "upgrading..."
sudo aptitude safe-upgrade -y
echo "autoremoving..."
sudo apt autoremove -y
}
#open mcomix
def mcx [file] {
bash -c $'mcomix "($file)" 2>/dev/null &'
}
#open file
def openf [file?] {
let file = if ($file | is-empty) {$in} else {$file}
bash -c $'xdg-open "($file)" 2>/dev/null &'
}
#search for specific process
def psn [name: string] {
ps | find $name
}
#kill specified process in name
def killn [name: string] {
ps | find $name | each {|| kill -f $in.pid}
}
#jdownloader downloads info (requires a jdown python script)
def nujd [] {
jdown | lines | each { |line| $line | from nuon } | flatten | flatten
}
# Switch-case like instruction
def switch [
var #input var to test
cases: record #record with all cases
#
# Example:
# let x = 3
# switch $x {
# 1: { echo "you chose one" },
# 2: { echo "you chose two" },
# 3: { echo "you chose three" }
# }
] {
echo $cases | get $var | do $in
}
#post to discord
def post_to_discord [message] {
let content = $"{\"content\": \"($message)\"}"
let weburl = "webhook_url"
post $weburl $content --content-type "application/json"
}
#select column of a table (to table)
def column [n] {
transpose | select $n | transpose | select column1 | headers
}
#get column of a table (to list)
def column2 [n] {
transpose | get $n | transpose | get column1 | skip 1
}
#short pwd
def pwd-short [] {
$env.PWD | str replace $nu.home-path '~'
}
#string repeat
def "str repeat" [count: int] {
each {|it| let str = $it; echo 1..$count | each {|| echo $str } }
}
#join 2 lists
def union [a: list, b: list] {
$a | append $b | uniq
}
#nushell source files info
def 'nu-sloc' [] {
let stats = (
ls **/*.nu
| select name
| insert lines { |it| open $it.name | str stats | get lines }
| insert blank {|s| $s.lines - (open $s.name | lines | find --regex '\S' | length) }
| insert comments {|s| open $s.name | lines | find --regex '^\s*#' | length }
| sort-by lines -r
)
let lines = ($stats | reduce -f 0 {|it, acc| $it.lines + $acc })
let blank = ($stats | reduce -f 0 {|it, acc| $it.blank + $acc })
let comments = ($stats | reduce -f 0 {|it, acc| $it.comments + $acc })
let total = ($stats | length)
let avg = ($lines / $total | math round)
$'(char nl)(ansi pr) SLOC Summary for Nushell (ansi reset)(char nl)'
print { 'Total Lines': $lines, 'Blank Lines': $blank, Comments: $comments, 'Total Nu Scripts': $total, 'Avg Lines/Script': $avg }
$'(char nl)Source file stat detail:'
print $stats
}
#go to dir (via pipe)
def --env goto [] {
let input = $in
cd (
if ($input | path type) == file {
($input | path dirname)
} else {
$input
}
)
}
#go to custom bash bin path, must be added last in config.nu
def --env goto-bash [] {
cd ($env.PATH | last)
}
#cd to the folder where a binary is located
def --env which-cd [program] {
let dir = (which $program | get path | path dirname | str trim)
cd $dir.0
}
#push to git
def git-push [m: string] {
git add -A
git status
git commit -a -m $"($m)"
git push origin main
}
#get help for custom commands
def "help my-commands" [] {
help commands | where is_custom == true
}
#web search in terminal (requires ddgr)
def gg [...search: string] {
ddgr -n 5 ($search | str join ' ')
}
#habitipy dailies done all (requires habitipy)
def hab-dailies-done [] {
let to_do = (habitipy dailies | grep | awk {print $1} | tr '.\n' ' ' | split row ' ' | into int)
habitipy dailies done $to_do
}
#update aliases backup file from config.nu
def update-aliases [] {
let nlines = (open $nu.config-path | lines | length)
let from = ((grep "## aliases" $nu.config-path -n | split row ':').0 | into int)
open $nu.config-path | lines | last ($nlines - $from + 1) | save /path/to/backup/file.nu
}
#update config.nu from aliases backup
def update-config [] {
let from = ((grep "## aliases" $nu.config-path -n | split row ':').0 | into int)
let aliases = "/path/to/backup/file.nu"
open $nu.config-path | lines | first ($from - 1) | append (open $aliases | lines) | save temp.nu
mv temp.nu $nu.config-path
}
#countdown alarm (requires termdown y mpv)
def countdown [
n: int # time in seconds
] {
let BEEP = "/path/to/sound/file"
let muted = (pacmd list-sinks | awk '/muted/ { print $2 }' | tr '\n' ' ' | split row ' ' | last)
if $muted == 'no' {
termdown $n;mpv --no-terminal $BEEP
} else {
termdown $n
unmute
mpv --no-terminal $BEEP
mute
}
}
#get aliases
def get-aliases [] {
open $nu.config-path | lines | find alias | find -v aliases | split column '=' | select column1 column2 | rename Alias Command | update Alias {|f| $f.Alias | split row ' ' | last} | sort-by Alias
}
#compress every subfolder into separate files and delete them
def `7zfolders` [] {
^find . -maxdepth 1 -mindepth 1 -type d -print0 | parallel -0 --eta 7z a -t7z -sdel -bso0 -bsp0 -m0=lzma2 -mx=9 -ms=on -mmt=on {}.7z {}
}
#compress to 7z using max compression
def `7zmax` [
filename: string #filename without extension
...rest: string #files to compress and extra flags for 7z (add flags between quotes)
#
# Example:
# compress all files in current directory and delete them
# 7zmax * "-sdel"
] {
if ($rest | is-empty) {
echo "no files to compress specified"
} else {
7z a -t7z -m0=lzma2 -mx=9 -ms=on -mmt=on $"($filename).7z" $rest
}
}
#add event to google calendar, also usable without arguments (requires gcalcli)
def addtogcal [
calendar? #to which calendar add event
title? #event title
when? #date: yyyy.MM.dd hh:mm
where? #location
duration? #duration in minutes
] {
let calendar = if $calendar == null {echo $"calendar: ";input } else {$calendar}
let title = if $title == null {echo $"\ntitle: ";input } else {$title}
let when = if $when == null {echo $"\nwhen: ";input } else {$when}
let where = if $where == null {echo $"\nwhere: ";input } else {$where}
let duration = if $duration == null {echo $"\nduration: ";input } else {$duration}
gcalcli --calendar $"($calendar)" add --title $"($title)" --when $"($when)" --where $"($where)" --duration $"($duration)" --default-reminders
}
#show gcal agenda in selected calendars
def agenda [
--full: int #show all calendars (default: 0)
...rest #extra flags for gcalcli between quotes (specified full needed)
#
# Examples
# agenda
# agenda --full true
# agenda "--details=all"
# agenda --full true "--details=all"
] {
let calendars = "your_selected_calendars"
let calendars_full = "most_calendars"
if ($full | is-empty) or ($full == 0) {
gcalcli --calendar $"($calendars)" agenda --military $rest
} else {
gcalcli --calendar $"($calendars_full)" agenda --military $rest
}
}
#show gcal week in selected calendards
def semana [
--full: int #show all calendars (default: 0)
...rest #extra flags for gcalcli between quotes (specified full needed)
#
# Examples
# semana
# semana --full true
# semana "--details=all"
# semana --full true "--details=all"
] {
let calendars = "your_selected_calendars"
let calendars_full = "most_calendars"
if ($full | is-empty) or ($full == 0) {
gcalcli --calendar $"($calendars)" calw $rest --military --monday
} else {
gcalcli --calendar $"($calendars_full)" calw $rest --military --monday
}
}
#show gcal month in selected calendards
def mes [
--full: int #show all calendars (default: 0)
...rest #extra flags for gcalcli between quotes (specified full needed)
#
# Examples
# mes
# mes --full true
# mes "--details=all"
# mes --full true "--details=all"
] {
let calendars = "your_selected_calendars"
let calendars_full = "most_calendars"
if ($full | is-empty) or ($full == 0) {
gcalcli --calendar $"($calendars)" calm $rest --military --monday
} else {
gcalcli --calendar $"($calendars_full)" calm $rest --military --monday
}
}
#get bitly short link (requires xclip)
def mbitly [longurl] {
if ($longurl | is-empty) {
echo "no url provided"
} else {
let Accesstoken = "Token"
let username = "user"
let url = $"https://api-ssl.bitly.com/v3/shorten?access_token=($Accesstoken)&login=($username)&longUrl=($longurl)"
let shorturl = (http get $url | get data | get url)
$shorturl
$shorturl | xclip -sel clip
}
}
#translate text using mymemmory api
def trans [
...search:string #search query]
--from:string #from which language you are translating (default english)
--to:string #to which language you are translating (default spanish)
#
#Use ISO standard names for the languages, for example:
#english: en-US
#spanish: es-ES
#italian: it-IT
#swedish: sv-SV
#
#More in: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
] {
if ($search | is-empty) {
echo "no search query provided"
} else {
let key = "api_kei"
let user = "user_email"
let from = if ($from | is-empty) {"en-US"} else {$from}
let to = if ($to | is-empty) {"es-ES"} else {$to}
let to_translate = ($search | str join "%20")
let url = $"https://api.mymemory.translated.net/get?q=($to_translate)&langpair=($from)%7C($to)&of=json&key=($key)&de=($user)"
http get $url | get responseData | get translatedText
}
}
#check if drive is mounted
def is-mounted [drive:string] {
let count = (ls "~/media" | find $"($drive)" | length)
if $count == 0 {
false
} else {
true
}
}
#get phone number from google contacts (requires goobook)
def get-phone-number [search:string] {
goobook dquery $search | from ssv | rename results | where results =~ '(?P<plus>\+)(?P<nums>\d+)'
}
#ping with plot (requires ttyplot)
def nu-png-plot [] {
bash -c "ping 1.1.1.1 | sed -u 's/^.*time=//g; s/ ms//g' | ttyplot -t \'ping to 1.1.1.1\' -u ms"
}
#plot download-upload speed (requires ttyplot and fast-cli)
def nu-downup-plot [] {
bash -c "fast --single-line --upload | stdbuf -o0 awk '{print $2 \" \" $6}' | ttyplot -2 -t 'Download/Upload speed' -u Mbps"
}
#plot data table using gnuplot
def gnu-plot [
data? #1 or 2 column table
--title:string #title
#
#Example: If $x is a table with 2 columns
#$x | gnu-plot
#($x | column 0) | gnu-plot
#($x | column 1) | gnu-plot
#($x | column 0) | gnu-plot --title "My Title"
#gnu-plot $x --title "My Title"
] {
let x = if ($data | is-empty) {$in} else {$data}
let n_cols = ($x | transpose | length)
let name_cols = ($x | transpose | column2 0)
let ylabel = if $n_cols == 1 {$name_cols | get 0} else {$name_cols | get 1}
let xlabel = if $n_cols == 1 {""} else {$name_cols | get 0}
let title = if ($title | is-empty) {if $n_cols == 1 {$ylabel | str upcase} else {$"($ylabel) vs ($xlabel)"}} else {$title}
$x | to tsv | save data0.txt
sed 1d data0.txt | save data.txt
gnuplot -e $"set terminal dumb; unset key;set title '($title)';plot 'data.txt' w l lt 0;"
rm data*.txt | ignore
}
# date string YYYY-MM-DD
def ymd [] {
(date now | format date %Y-%m-%d)
}
# date string DD-MM-YYYY
def dmy [] {
(date now | format date %d-%m-%Y)
}
# create directory and cd into it.
def --env md [dir] {
mkdir $dir
cd $dir
}
# Fuzzy finds a value in a newline-separated-string or a list, using an
# optional preview. If the string or the list contains only one item,
# it is returned immediately.
# Requires the external binary 'skim'.
#
# Examples:
# > "a\nb\n" | skim
# > ls | get name | skim --preview 'ls --color {}'
def skim [
--preview (-p) = '' # command to use for the sk preview
] {
let lst = $in
let type = ($lst | describe)
let s = (if ($type | str starts-with 'list<') {
$lst | str join (char nl)
} else if ($type == 'string') {
$lst
})
if ($s | is-empty) {
null
} else {
if ($preview | is-empty ) {
($s
| sk
--layout reverse
--preview-window down:65%
--select-1
| str trim)
} else {
($s
| sk
--layout reverse
--preview-window down:65%
--select-1
--preview $preview
| str trim)
}
}
}
# Group list values that match the next-group regex.
# This function is a useful helper to quick and dirty parse data
# that contains line-wise a 'header', followed by a variable number
# of data entries. The return value is a table of header-keys with
# a list of values in the second column. Values before a header-key
# and header-keys without values are ignored.
#
# Example:
# [id_a 1 2 id_b 3] | group-list '^id_'
def group-list [
regex # on match, a new group is created
] {
let lst = $in
def make-group [v, buf, ret, key] {
let new_group = ($'($v)' =~ $regex)
if $new_group {
let is_key = (not ($key | is-empty))
let is_buf = (not ($buf | is-empty))
if ($is_buf and $is_key) {
let ret = ($ret | append {key: $key, values: $buf})
{buf: [], ret: $ret, key: $v}
} else {
{buf: [], ret: $ret, key: $v}
}
} else {
let buf = ($buf | append $v)
{buf: $buf, ret: $ret, key: $key}
}
}
def loop [lst, buf=[], ret=[], key=''] {
if ($lst | is-empty) {
{ret: $ret, buf: $buf, key: $key}
} else {
let v = ($lst | first)
let obj = (make-group $v $buf $ret $key)
let rest = ($lst | skip)
loop $rest $obj.buf $obj.ret $obj.key
}
}
let obj = (loop $lst)
let ret = $obj.ret
let buf = $obj.buf
let key = $obj.key
let is_key = (not ($key | is-empty))
let is_buf = (not ($buf | is-empty))
if ($is_buf and $is_key) {
$ret | append {key: $key, values: $buf}
} else {
$ret
}
}

View File

@@ -0,0 +1,92 @@
# Nushell Password Generator "nupass"
This nushell command randomly retrieves a specified number of words from a dictionary file (English with Japanese words added by @rickcogley) less than or equal to a given parameter's length, formats the words randomly with capitalization, then separates the words with some random symbols and numbers to return a password.
To use:
1. Get the dictionary file to your system using nushell's `http`:
```
http get https://raw.githubusercontent.com/RickCogley/jpassgen/master/genpass-dict-jp.txt | save genpass-dict-jp
```
...which has also been included in this folder for convenience.
2. Confirm your `$env.NU_LIB_DIRS` location, and copy the below script `2. nupass.nu` there as `nupass.nu`.
3. Set the script as executable like `chmod +x nupass.nu`
4. Specify the dictionary file's location in the script:
```
let dictfile = $"/path/to/my/genpass-dict-jp"
```
5. In the main function's flags section, confirm and edit the default symbols list, diceware delimiter and threads for par-each (double the number of your CPU cores seems to be a good sweet spot):
```
--symbols (-s): string = "!@#$%^&()_-+[]{}" # Symbols to use in password
--delimiter (-m): string = "-" # Delimiter for diceware
--threads (-t): int = 16 # Number of threads to use in par-each
```
6. Load the script with `use` in your `config.nu`, something like:
```
use nupass.nu
```
(you can specify the path as `use /path/to/nupass.nu` if you're not taking advantage of `$env.NU_LIB_DIRS`)
Reload nu, then run it to test:
```
nupass -h
nupass 5
nupass 6 --debug
nupass 8 -v diceware
nupass 8 -v diceware -m _
nupass 4 -v mixnmatch
nupass 6 -v alphanum
nupass 5 -l 8
```
### Testing
If you're making changes to the script while testing, you can just re-source the script by doing:
`use nupass.nu`
... which will reload the latest you have saved.
From `nu` version 0.79.1, you can use the standard library, and use its bench command to do a benchmark. Load the standard library by adding `use std` in your `env.nu`, reload, then assuming `nupass.nu` is in your path, you can benchmark like so:
```
std bench --rounds 10 --verbose {nupass 10}
std bench --rounds 10 --verbose {nupass 100 -v diceware}
std bench --rounds 10 --verbose {nupass 1000 -v mixnmatch}
```
If you change the `par-each` to `each` in the main list builders for instance, you'll see a significant performance hit. When I benchmarked `nupass 100`, using just `each` took 7 sec per round, whereas changing to `par-each` dropped that to about 1 sec per round.
You can tweak it a little further by setting the threads for par-each.
```
std bench --rounds 10 --verbose {nupass 100 -v diceware -t 8}
std bench --rounds 10 --verbose {nupass 100 -v diceware -t 16}
std bench --rounds 10 --verbose {nupass 100 -v diceware -t 32}
```
<img width="736" alt="image" src="https://user-images.githubusercontent.com/512328/235553238-48b48f37-0eae-48d3-8afe-e17515cd8325.png">
### Caveats
I've been scripting for quite a long time, but not in nu. Input appreciated to make this more nu-esque! The script is in a decent place as of the 20230501 version.
Obviously you can just use the `random chars` or `random integers` commands but I like to have words I can read in my passwords, and I think those generated by this script have sufficient entropy.
This command doesn't let you specify a precise length.
### Acknowledgements
Thanks everyone on Discord for putting up with and answering my nubie questions @amtoine, @fdncred, @jelle, @sygmei, @kubouch and for the feedback after try number 1.
<img width="576" alt="image" src="https://user-images.githubusercontent.com/512328/235383307-d3f3d65d-c184-4dfa-9fe9-677b677d8531.png">

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,120 @@
# Script to generate a password from a dictionary file
# Author: @rickcogley
# Thanks: @amtoine, @fdncred, @jelle, @sygmei, @kubouch
# Updates: 20230415 - initial version
# 20230416 - add @amtoine's slick probabilistic "random decimal" char capitalization
# 20230417 - add script duration output in debug block
# 20230421 - add length of symbol chars to get-random-symbol function
# 20230422 - add variant flag to generate different styles of passwords
# 20230501 - refactor to allow number of words to be specified, use list manipulation and reduce to string
# 20230502 - improve performance on list builders with par-each
# 20230503 - add threads flag for fine tuning par-each
#======= NUPASS PASSWORD GENERATOR =======
# Generate password of 3 dictionary file words, numbers and symbols
export def main [
words: int = 3 # Number of words in password
--word_length (-l): int = 5 # Max length of words in password
--symbols (-s): string = "!@#$%^&()_-+[]{}" # Symbols to use in password
--variant (-v): string = "regular" # Password style to generate in regular, mixnmatch, alphanum, alpha, diceware
--delimiter (-m): string = "-" # Delimiter for diceware
--threads (-t): int = 16 # Number of threads to use in par-each
--debug (-d) # Include debug info
] {
##### Main function #####
# Get dictionary file:
# http get https://raw.githubusercontent.com/RickCogley/jpassgen/master/genpass-dict-jp.txt | save genpass-dict-jp
# Set the path:
let dictfile = $"/usr/local/bin/genpass-dict-jp"
let starttime = (date now)
# Find number of lines with strings less than or equal to the supplied length
let num_lines = (open ($dictfile) | lines | wrap word | upsert len {|it| $it.word | split chars | length} | where len <= ($word_length) | length)
# Get random words from dictionary file
let random_words = (1..$words | par-each { |i| $dictfile | get-random-word $word_length $num_lines | random-format-word } --threads $threads)
# Get some symbols to sprinkle like salt bae
# Update default symbol chars in symbols flag
let symbols_len = ($symbols | str length)
let random_symbols = (1..$words | par-each { |i| $symbols | get-random-symbol $symbols $symbols_len } --threads $threads)
# Get some random numbers
let random_numbers = (1..$words | par-each { |i| (random int 0..99) } --threads $threads)
# Print some vars if debug flag is set
if $debug {
print $"(ansi bg_red) ====== DEBUG INFO ====== (ansi reset)"
print $"(ansi bg_blue) 🔔 Number of lines in dict with words under ($word_length) chars: (ansi reset)"
print $num_lines
print $"(ansi bg_blue) 🔔 Words from randomly selected lines: (ansi reset)"
print $random_words
print $"(ansi bg_blue) 🔔 Randomly selected symbols: (ansi reset)"
print $random_symbols
print $"(ansi bg_blue) 🔔 Randomly selected numbers: (ansi reset)"
print $random_numbers
let endtime = (date now)
print $"(ansi bg_green) 🔔 Generated password in ($endtime - $starttime): (ansi reset)"
}
# Return password in selected variant
if $variant == "regular" {
# Default variant, with regular distribution
# Generate new list w symbol, words, numbers, then reduce to string
return (0..($words - 1) | each { |it| ($random_symbols | get $it) + ($random_words | get $it) + ($random_numbers | get $it | into string) } | reduce { |it, acc| $acc + $it })
} else if $variant == "mixnmatch" {
# Combine lists, shuffle randomly, reduce to string
return (($random_words ++ $random_symbols ++ $random_numbers | shuffle) | reduce { |it, acc| ($acc | into string) + ($it | into string) })
} else if $variant == "alphanum" {
# Combined random int and random word, reduce to string
return (0..($words - 1) | each { |it| (random int 0..99 | into string) + ($random_words | get $it) } | reduce { |it, acc| $acc + $it })
} else if $variant == "alpha" {
# Reduce random words only to string
return ($random_words | reduce { |it, acc| $acc + $it })
} else if $variant == "diceware" {
# Reduce to string with hyphen between words
return ($random_words | reduce { |it, acc| $acc + $"($delimiter)($it)" })
}
}
##### Utility functions #####
# Function to get random word from a dictionary file
def get-random-word [
wordlength: int
numlines: int
] {
open
| lines
| wrap word
| upsert len {|it| $it.word | str length}
| where len <= ($wordlength)
| get (random int 1..($numlines))
| get word
}
# Function to format a word randomly
def random-format-word [] {
par-each {|it|
let rint = (random int 1..4)
if $rint == 1 {
($it | str capitalize)
} else if $rint == 2 {
($it | str upcase)
} else if $rint == 3 {
($it | split chars | each {|c| if (random float) < 0.2 { $c | str upcase } else { $c }} | str join "")
} else {
($it | str downcase)
}
}
}
# Function to get random symbol from list of symbols
def get-random-symbol [
symbolchars: string
symbolcharslen: int
] {
$symbolchars
| split chars
| get (random int 0..($symbolcharslen - 1))
}

View File

@@ -0,0 +1,52 @@
let table = (echo [
[url user_login title];
[https://api.github.com/repos/nushell/nushell/issues/3382 ammkrn 'Dont unwrap rustyline helper in cli']
[https://api.github.com/repos/nushell/nushell/issues/3379 jonathandturner 'Simplify down to one type of context']
[https://api.github.com/repos/nushell/nushell/issues/3377 kubouch 'Port range to engine-p']
[https://api.github.com/repos/nushell/nushell/issues/3375 fdncred 'added check for endian-ness, added a bytes and skip']
[https://api.github.com/repos/nushell/nushell/issues/3374 fdncred 'added ability to change ']
[https://api.github.com/repos/nushell/nushell/issues/3370 fdncred 'add nu-pretty-hex, add into binary, update binaryview']
[https://api.github.com/repos/nushell/nushell/issues/3367 fdncred 'tweaked the error handling to show specific errors']
])
# Show what the table looks like
print $"This is an example table (char nl)"
print $table
print $"This is markdown created from the example table (char nl)"
# Now show what the table in Markdown looks like
print $"## Nushell(char nl)(char nl)"
print ($table | group-by user_login | transpose user prs | each { |row|
let user_name = $row.user
let pr_count = (echo $row.prs | length)
# only print the comma if there's another item
let user_prs = ($row.prs | enumerate | each { |pr|
if $pr_count == ($pr.index + 1) {
echo $'[($pr.item.title)](char lp)($pr.item.url)(char rp)'
} else {
echo $'[($pr.item.title)](char lp)($pr.item.url)(char rp), and '
}
} | str join)
echo $"- ($user_name) created ($user_prs) (char nl)"
} | str join)
# ╭───┬──────────────────────────────────────────────────────────┬─────────────────┬───────────────────────────────────────────────────────╮
# │ # │ url │ user_login │ title │
# ├───┼──────────────────────────────────────────────────────────┼─────────────────┼───────────────────────────────────────────────────────┤
# │ 0 │ https://api.github.com/repos/nushell/nushell/issues/3382 │ ammkrn │ Dont unwrap rustyline helper in cli │
# │ 1 │ https://api.github.com/repos/nushell/nushell/issues/3379 │ jonathandturner │ Simplify down to one type of context │
# │ 2 │ https://api.github.com/repos/nushell/nushell/issues/3377 │ kubouch │ Port range to engine-p │
# │ 3 │ https://api.github.com/repos/nushell/nushell/issues/3375 │ fdncred │ added check for endian-ness, added a bytes and skip │
# │ 4 │ https://api.github.com/repos/nushell/nushell/issues/3374 │ fdncred │ added ability to change "#" color using header_color │
# │ 5 │ https://api.github.com/repos/nushell/nushell/issues/3370 │ fdncred │ add nu-pretty-hex, add into binary, update binaryview │
# │ 6 │ https://api.github.com/repos/nushell/nushell/issues/3367 │ fdncred │ tweaked the error handling to show specific errors │
# ╰───┴──────────────────────────────────────────────────────────┴─────────────────┴───────────────────────────────────────────────────────╯
def log [message:any] {
let now = (date now | format date '%Y%m%d_%H%M%S.%f')
let mess = ([$now '|DBG|' $message (char newline)] | str join)
echo $mess
}

View File

@@ -0,0 +1,5 @@
# Nu_101 Scripts
### Definition
These scripts should be used to demonstrate to the beginner how nushell scripting works. Perhaps how it's different from Windows `batch` files and `bash` shell scripts. Also, to show off how nushell pipes commands in and out of one another.

View File

@@ -0,0 +1,46 @@
### These examples are here to show the user
### How the parser works by showing examples that will not parse
### The beginning nushell script writer / user may get confused
### as to why their script is not working
###
### If you uncomment any of the defs below these are
### the error messages you will see
#
#
#
### Examples p1 - p3 below elucidate the following idea:
### That a brace has to be on the same line as the def
###
###
### Error: nu::parser::missing_positional
### The error message is: "Missing required positional argument"
### missing block
###
### help: Usage: def <def_name> <params> <block>
###
#
### https://github.com/nushell/nushell/issues/2972
#
### All of these examples will not parse.
### def p1 [arg]
### { echo $arg }
### def p2 [arg]
### {
### echo $arg }
### def p3 [arg]
### {
### echo $arg
### }
### This breaks because you need a space between
### between foo and the left bracket
### def foo[] {
### "bar"
### }
### This works
### def foo [] {
### "bar"
### }

View File

@@ -0,0 +1,5 @@
def my-ls [x] {
ls $x | where size > 10kb
}
my-ls .

View File

@@ -0,0 +1,98 @@
# Some examples of how you can use nushell commands to treat lists like other data structures and perform equivalent data structure operations on them.
## Queue (first in, first out [FIFO])
let queue = [1 2 3]
let elem = 4
### Enqueue (push)
$queue | append $elem
### Dequeue (shift)
{ out: ($queue | first),
new_queue: ($queue | skip 1) }
## Stack (last in, first out [LIFO])
### Push
$queue | append $elem
### Pop
{ out: ($queue | last),
new_stack: ($queue | drop) }
## Set
# Ordered sets are similar to below, just taking more care of order when altering the sets since lists are already ordered.
let set = [1 2 3]
let elem = 4
let set_b = [2 3 4 5]
### Checking set membership
$elem in $set # false
2 in $set # true
### Inserting a new element
if $elem not-in $set { $set | append $elem }
# or
$set | append $elem | uniq
# Result: [1 2 3 4]
### Union
$set ++ $set_b | uniq
# Result: [1 2 3 4 5]
### Intersection
$set | filter { |elem| $elem in $set_b }
# Result: [2 3]
### Difference
# $set - $set_b
$set | filter { |elem| $elem not-in $set_b }
# or
# Result: [1]
### Symmetric Difference
$set ++ $set_b | uniq --unique
# Result: [1 4 5]
### Multiset (bag)
# Pretty much the same as a list but you can get the counts of the multiset elements with
[1 2 2 3] | uniq --count
# Result:
# ╭───┬───────┬───────╮
# │ # │ value │ count │
# ├───┼───────┼───────┤
# │ 0 │ 1 │ 1 │
# │ 1 │ 2 │ 2 │
# │ 2 │ 3 │ 1 │
# ╰───┴───────┴───────╯
# The unique values along with how many times they are in the multiset/bag.

View File

@@ -0,0 +1,74 @@
### So in this case you have to pass in a parameter
### Any parameter you type will work
### If you don't type a parameter you get an error
###
### The syntax for this is
### noflag hola
###
def noflag [x] {
echo $x
}
### The syntax for this is
### flag -f
### flag --flag
### If you type anything else it does not work
### For example
### flag -flag
### flag -f=hola
### flag -f hola
### flag -f = hola
def flag [
--flag(-f)
] {
echo $flag
}
# Write out the flags you entered
def flag_details [myint: int, mystring: string] {
echo "myint is " $myint | str join
echo "mystring is " $mystring | str join
}
# Get the data passed into the flags
def get_flag [
--test_int(-i): int # The test intlocation
--test_string(-s): string # The test string
] {
let is_int_empty = ($test_int == null)
let is_string_empty = ($test_string == null)
let no_int_no_string = ($is_int_empty == true and $is_string_empty == true)
let no_int_with_string = ($is_int_empty == true and $is_string_empty == false)
let with_int_no_string = ($is_int_empty == false and $is_string_empty == true)
let with_int_with_string = ($is_int_empty == false and $is_string_empty == false)
echo 'no int and no string ' $no_int_no_string | str join
echo 'no int with string ' $no_int_with_string | str join
echo 'with int and no string ' $with_int_no_string | str join
echo 'with int and with string ' $with_int_with_string | str join
if $no_int_no_string {
(flag_details 1 "blue")
} else if $no_int_with_string {
(flag_details 1 $test_string)
} else if $with_int_no_string {
(flag_details $test_int "blue")
} else if $with_int_with_string {
(flag_details $test_int $test_string)
}
}
# To run this call
# > get_flag
# it will default to int 1 and string blue
# > get_flag -i 2
# This changes to int 2 and string blue
# > get_flag -i 3 -s green
# This changes to int 3 and string green

View File

@@ -0,0 +1,11 @@
# This is an experiment to see if one can have
# $it in and inner loop and an outer loop at
# the same time, each having different values
seq 30 39 | each { |outer|
let row = $"($outer) "
let data = (seq 40 49 | each { |inner|
$"($inner) "
} | str join)
$"($row)($data)"
} | str join (char newline)

View File

@@ -0,0 +1,24 @@
# This checks the -f switch to see if it was supplied
# and tests the new $nothing variable
def nada [
flat?
] {
if $flat == null {
true
} else {
false
}
}
# This shows an alternate way to check for nothing
def nada2 [
flat?
] {
let flat = ($flat | is-empty)
if $flat {
true
} else {
false
}
}

View File

@@ -0,0 +1,47 @@
# construct bars based of a given percentage from a given width (5 is default)
# > bar 0.2
# █
# > bar 0.71
# ███▌
def 'bar' [
percentage: float
--background (-b): string = 'default'
--foreground (-f): string = 'default'
--progress (-p) # output the result using 'print -n'
--width (-w): int = 5
] {
let blocks = [null "▏" "▎" "▍" "▌" "▋" "▊" "▉" "█"]
let $whole_part = (($blocks | last) * ($percentage * $width // 1))
let $fraction = (
$blocks
| get (
($percentage * $width) mod 1
| $in * ($blocks | length | $in - 1)
| math round
)
)
let result = (
$"($whole_part)($fraction)"
| fill -c $' ' -w $width
| if ($foreground == 'default') and ($background == 'default') {} else {
$"(ansi -e {fg: ($foreground), bg: ($background)})($in)(ansi reset)"
}
)
if $progress {
print -n $"($result)\r"
} else {
$result
}
}
use std assert equal
#[test]
def bar_tests [] {
equal "█▌ " (bar 0.3)
equal "███ " (bar 0.3 --width 10)
equal "▊" (bar 0.71 --width 1)
equal "███████▏ " (bar 0.71 --width 10)
}

View File

@@ -0,0 +1,23 @@
def loading [] {
print -n $"Loading (char newline)"
0..100 | each { |tick|
sleep 50ms
# I believe '1000D' means move the cursor to the left 1000 columns
print -n $"(ansi -e '1000D')($tick)%"
}
#show_cursor
}
def show_cursor [] {
print $"(ansi -e '?25h')"
}
def hide_cursor [] {
print $"(ansi -e '?25l')"
}
def demo_percent_meter [] {
hide_cursor
loading
show_cursor
}

View File

@@ -0,0 +1,52 @@
# progress bar attempt
# https://askubuntu.com/questions/747143/create-a-progress-bar-in-bash
# https://www.shellscript.sh/tips/progressbar/
# There is a strange artifact drawing the first two full blocks
# You can see this artifact better in progress_bar_no_back.nu
# I'm not sure what's going on nor how to fix it.
let pb_len = 25
let bg_fill = "▒" # Fill up to $pb_len
let blocks = ["▏" "▎" "▍" "▌" "▋" "▊" "▉" "█"]
# "█" #8/8
# "▉" #7/8
# "▊" #3/4
# "▋" #5/8
# "▌" #1/2
# "▍" #3/8
# "▎" #1/4
# "▏" #1/8
# Turn off the cursor
ansi cursor_off
# Move cursor all the way to the left
print -n $"(ansi -e '1000D')"
# Draw the background for the progress bar
print -n ($bg_fill | fill -c $bg_fill -w $pb_len -a r)
1..<$pb_len | each { |cur_progress|
# This is kind of a hack because it's not incrementally drawing a new box
# It's drawing the entire row every time with a different padding amount
# echo $blocks.7 | fill --character $blocks.7 --width $it --align right
0..7 | each { |tick|
let cur_idx = ($tick mod 8)
let cur_block = (echo $blocks | get $cur_idx)
print -n $"(ansi -e '1000D')($cur_block | fill -c $blocks.7 -w $cur_progress -a r)"
sleep 20ms
}
print -n $"(ansi -e '1000D')"
}
# Fill in the last background block
print $"($blocks.7 | fill -c $blocks.7 -w $pb_len -a r)"
"Done"
ansi cursor_on
# Try to do this in the next version
# Make it a custom command so you can do
# set-progress 33 100
# and the display look like
# 33% (33/100) [███████████ ]

View File

@@ -0,0 +1,16 @@
let blocks = ["▏" "▎" "▍" "▌" "▋" "▊" "▉" "█"]
let pb_size = 25
ansi cursor_off
1..<$pb_size | each { |cur_size|
0..7 | each { |tick|
let idx = ($tick mod 8)
let cur_block = ($blocks | get $idx)
print -n $"(ansi -e '1000D')($cur_block | fill -c $blocks.7 -w $cur_size -a r)"
sleep 20ms
}
print -n $"(ansi -e '1000D')"
}
print $"($blocks.7 | fill -c $blocks.7 -w $pb_size -a r)"
'Done'
ansi cursor_on

View File

@@ -0,0 +1,27 @@
# Runs C code via GCC without leaving a file behind
def rcc [
file: path # The file to run
] {
# Remove exe if still exists
rm $"($file).exe" --permanent --force
# Compile code to exe
^gcc ("." | path join $file | path expand) -o ("." | path join $"($file).exe" | path expand)
# Execute exe
^$"($file).exe"
# Remove exe
rm $"($file).exe" --permanent --force
}
# Runs C++ code via g++ without leaving a file behind
def r++ [
file: path # The file to run
] {
# Remove exe if still exists
rm $"($file).exe" --permanent --force
# Compile code to exe
^g++ ("." | path join $file | path expand) -o ("." | path join $"($file).exe" | path expand)
# Execute exe
^$"($file).exe"
# Remove exe
rm $"($file).exe" --permanent --force
}

View File

@@ -0,0 +1,72 @@
# Convert Fahrenheit to Celsius
export def f-to-c [
fahren: number # Degrees Fahrenheit
--round(-r): int = 2 # Digits of precision to round to
] {
# (100°F 32) × 5/9 = 37.778°C
let $n = if ($fahren | describe) == "float" {$fahren} else {$fahren | into float }
let celsius = ((( $n - 32.) * 5 / 9. ) | math round -p $round )
$"($fahren) °F is ($celsius) °C"
}
# Convert Fahrenheit to Kelvin
export def f-to-k [
fahren: number # Degrees Fahrenheit
--round(-r): int = 2 # Digits of precision to round to
] {
# (100°F 32) × 5/9 + 273.15 = 310.928K
let $n = if ($fahren | describe) == "float" {$fahren} else {$fahren | into float }
let kelvin = ((($n - 32) * 5 / 9 + 273.15)| math round -p $round )
$"($fahren) °F is ($kelvin) °K"
}
# Convert Celsius to Fahrenheit
export def c-to-f [
celsius: number # Degrees Celsius
--round(-r): int = 2 # Digits of precision to round to
] {
# (100°C × 9/5) + 32 = 212°F
let $n = if ($celsius | describe) == "float" {$celsius} else {$celsius | into float }
let fahren = ((($n * 9 / 5) + 32) | math round -p $round )
$"($celsius) °C is ($fahren) °F"
}
# Convert Celsius to Kelvin
export def c-to-k [
celsius: number # Degrees Celsius
--round(-r): int = 2 # Digits of precision to round to
] {
# 100°C + 273.15 = 373.15K
let $n = if ($celsius | describe) == "float" {$celsius} else {$celsius | into float }
let kelvin = (($n + 273.15) | math round -p $round )
$"($celsius) °C is ($kelvin) °K"
}
# Convert Kelvin to Fahrenheit
export def k-to-f [
kelvin:number # Degrees Fahrenheit
--round(-r): int = 2 # Digits of precision to round to
] {
# (100K 273.15) × 9/5 + 32 = -279.7°F
let $n = if ($kelvin | describe) == "float" {$kelvin} else {$kelvin | into float }
let fahren = ((($n - 273.15) * 9 / 5 + 32) | math round -p $round )
$"($kelvin) °K is ($fahren) °F"
}
# Convert Kelvin to Celsius
export def k-to-c [
kelvin:number # Degrees Celsius
--round(-r): int = 2 # Digits of precision to round to
] {
# 100K 273.15 = -173.1°C
let $n = if ($kelvin | describe) == "float" {$kelvin} else {$kelvin | into float }
let celsius = (($n - 273.15) | math round -p $round )
$"($kelvin) °K is ($celsius) °C"
}

View File

@@ -0,0 +1,78 @@
# (See TODO.md for more details)
# This first command looks for the file with the todo list and
# prints it to the screen.
def --env printer [] {
let contents = (
# if you haven't setup this environment var,
# replace `$env.TODO` with the path to your
# `todo.txt`
open $env.TODO
| split row "\n"
| take (($in | length) - 1)
| each {|$it, n| $"($n + 1) (ansi red)->(ansi reset) ($it)"}
| str join "\n"
)
# change the message to print what you want
if $contents == "" {
echo $"\n(ansi lgb)Everything's been done!!! Yay!!!(ansi reset)\n"
} else {
echo $"\n(ansi lgb)You promised to do these(ansi reset)\n($contents)"
}
}
printer # don't forget to call it here, so that it gets run automatically
# This command is used to update you todo-list, and should ideally be
# sourced in your `config.nu`
def todo [
--edit(-e) # edit todo manually
--add(-a): string # add item
--remove(-r): int # remove an item using its number
--clear(-c) # clear the list
] {
def get_todo [] {
# if you haven't setup this environment var,
# replace `$env.TODO` with the path to your
# `todo.txt`
open $env.TODO
| split row "\n"
| take (($in | length) - 1)
}
def todo_add [todo: string] {
get_todo
| reverse
| $in ++ [$todo]
| reverse
| save $env.TODO -f
}
def todo_rm [num: int] {
get_todo
| reverse
# change ($num - 1) to $num to have zero-based indexing
| filter {|it, n| $n != ($num - 1) }
| reverse
| save $env.TODO -f
}
if $edit {
clear
vim $env.TODO # replace `vim` with your favorite editor
} else if ($add != null) {
clear
todo_add $add | todo
} else if ($remove != null) {
clear
todo_rm $remove | todo
} else if $clear {
clear
# change the message to print what you want
"" | save $env.TODO -f | echo $"(ansi lgb)todo cleared!!!"
} else {
clear
printer
}
}

View File

@@ -0,0 +1,92 @@
# give the type of the input data in a structured form
def typeof [] {
let data = $in
let raw_type = $data | describe
match ($raw_type | str replace --regex "<.*" "") {
"list" => { {
type: "list"
items: ($raw_type | parse "list<{type}>" | get type.0)
} },
"record" => {
type: "record"
fields: ($data | columns | each {|field| {
name: $field,
type: ($data | get $field | typeof)
} } | transpose -rid)
},
"table" => {
type: "table"
columns: ($data | columns | each {|col| {
name: $col,
type: ($data | get $col | describe | parse "list<{type}>" | get type.0)
} } | transpose -rid)
},
_ => $raw_type
}
}
#[test]
def simple_type [] {
use std assert
assert equal ("foo" | typeof) "string"
assert equal (123 | typeof) "int"
assert equal (true | typeof) "bool"
}
#[test]
def list_type [] {
use std assert
assert equal ([1 2 3] | typeof) {type: "list", items: "int"}
assert equal (["foo" "bar" "baz"] | typeof) {type: "list", items: "string"}
assert equal (["foo" 2 true] | typeof) {type: "list", items: "any"}
}
#[test]
def table_type [] {
use std assert
assert equal (ls | typeof) {
type: "table",
columns: {
name: "string",
type: "string",
size: "filesize",
modified: "date",
}
}
}
#[test]
def record_type [] {
use std assert
assert equal ($nu | typeof) {
type: "record",
fields: {
default-config-dir: "string",
config-path: "string",
env-path: "string",
history-path: "string",
loginshell-path: "string",
plugin-path: "string",
home-path: "string",
temp-path: "string",
pid: "int",
os-info: {
type: "record",
fields: {
name: "string",
arch: "string",
family: "string",
kernel_version: "string",
}
},
startup-time: "duration",
is-interactive: "bool",
is-login: "bool",
current-exe: "string",
}
}
}

View File

@@ -0,0 +1,37 @@
# The purpose of this module is to automatically update Path variable on Windows since Windows is unable to do it on its own forcing users to restart terminal to pick up updates
# Usage: import this into your config.nu and then add the update-path function to your pre_prompt hook
module update-path {
def parse-paths [] {
where name == Path
| get value.0
| str trim -c (char double_quote)
| split row (char esep)
| par-each {|path|
let suffix = if $path ends-with (char path_sep) {(char path_sep)} else {''} # necessary because nushell strips trailing path separators which breaks uniq later on
$path
| path split
| each {|elem|
if $elem starts-with '%' and $elem ends-with '%' {
$env
| get ($elem|str trim -c '%')
} else {
$elem
}
}
| path join
| append $suffix
| str join
}
}
def get-paths-from-registry [] {
registry query --hkcu environment
| parse-paths
| append (registry query --hklm 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'| parse-paths)
}
export def --env update [] {
$env.Path = ($env.Path|append (get-paths-from-registry)|uniq)
}
}

View File

@@ -0,0 +1,63 @@
#!/usr/bin/nu
# /etc/hosts update handler
module hosts {
def display_heads [old, new] {
echo "Current file:"
echo $old
echo "New file:"
echo $new
print ""
}
def are_the_same [old_head, new_head] {
(($old_head | first) == ($new_head | first))
}
# Updater function for /etc/hosts
export def update [
--force (-f) # force replace /etc/hosts
] {
# just sample values, feel free to change it but note that it works for StevenBlack files
let LINK = "https://raw.githubusercontent.com/StevenBlack/hosts/master/alternates/gambling-porn/hosts"
let WHITELIST = ["multipasko"]
let BLACKLIST = ["0.0.0.0 tiktok.com"]
let whitelisted = "(" + ($WHITELIST | str join "|") + ")"
let pattern = ($"0.0.0.0.*($whitelisted).*$")
let OLD_FILE = "/etc/hosts"
let TMP_FILE = (http get $LINK | lines)
if ($env.LAST_EXIT_CODE == 0) {
let OLD_HEAD = (open $OLD_FILE --raw | lines | first 8 | last 3)
let TMP_HEAD = ($TMP_FILE | first 8 | last 3)
display_heads $OLD_HEAD $TMP_HEAD
if (not ((are_the_same $OLD_HEAD $TMP_HEAD) and (not $force))) {
echo "Do you want to update the /etc/hosts file? [Y/n]"
let choice = (input)
if $choice in ["" "Y" "y"] {
let TMP_FILE = if ($WHITELIST|is-empty) {
($TMP_FILE)
} else {
($TMP_FILE | where {|line| $line !~ $pattern})
}
let TMP_FILE = ($TMP_FILE | append $BLACKLIST)
$TMP_FILE | save /tmp/temphostsfile
if ($env.LAST_EXIT_CODE == 0) {
sudo mv /tmp/temphostsfile $OLD_FILE
echo "Done!"
} else {
error make -u {msg: "Something went wrong while overwriting the /etc/hosts file"}
}
}
} else {
echo "No updates available."
}
} else {
error make -u {msg: "Failed downloading the hosts file, try again."}
}
}
}

View File

@@ -0,0 +1,5 @@
# Web Scraping
### Definition
Simple scripts to demonstrate how to scrape websites in nushell. Requires `query web` plugin

View File

@@ -0,0 +1,16 @@
#!/usr/bin/env nu
# script to get anagrams with scrabble points from unscramble.me
# NOTE: this is just a small show case of piping query web stuff
def main [...words: string] {
let base = "https://www.unscramble.me/"
$words | par-each {
|word|
http get ($base + $word)
|query web -q ".mBottom-6" -m # gets the anagram table part of the page
|drop nth 0 # remove the description/definition of "words"
|first # we only care about the biggest/first anagrams (which is the length of the input word)
|query web -q "table" -m # get the html table
|to text # we need it as raw html to parse it
|query web --as-table ["Word" "Scrabble points" "Words with friends points"] # parse the html table as table
}
}

View File

@@ -0,0 +1,21 @@
#!/usr/bin/env nu
let baseurl = 'https://www.schiit.co.uk/'
let pages = ['headphone-amps' 'dacs' 'schiit-gaming-products' 'power-amplifiers' 'preamps' 'upgrades' 'accessories-cables' 'schiit%20graded%20stock']
# Simple script to check stock of https://schiit.co.uk store
def main [] {
$pages | par-each { |page|
http get ($baseurl + $page)
|query web -q '.price, .stock, .product-item h5'
|str trim
|group 3
|each {
|x| {
name: $x.0,
avail: $x.1,
price: $x.2
}
}
}
|sort-by avail
}

View File

@@ -0,0 +1,42 @@
let shell_list = [
[name repo];
[bash bminor/bash]
[fish fish-shell/fish-shell]
[nushell nushell/nushell]
# [powershell no-github-url]
[pwsh PowerShell/PowerShell]
[ksh2020 ksh2020/ksh]
[ksh93u att/ast]
# [csh no-github-url]
# [dash no-github-url]
# [sh no-github-url]
# [cmd no-github-url]
[aws-shell awslabs/aws-shell]
[azure-cloud-shell Azure/CloudShell]
[elvish elves/elvish]
[es wryun/es-shell]
[ion redox-os/ion]
[MirBSDksh MirBSD/mksh]
[ngs ngs-lang/ngs]
[openbsd_ksh ibara/oksh]
[oil oilshell/oil]
[shell++ alexst07/shell-plus-plus]
[tcsh tcsh-org/tcsh]
[xonsh xonsh/xonsh]
[yash magicant/yash]
[zsh zsh-users/zsh]
]
$shell_list | each { |r|
print -n $"Working on ($r.name)"
sleep 250ms
if ($r.repo | str starts-with no) {
[[shell repo stars]; [($r.name) "no github url" 0]]
print ""
} else {
let url = $"https://api.github.com/repos/($r.repo)"
let count = (http get -u $env.GITHUB_USERNAME -p $env.GITHUB_PASSWORD ($url) | get stargazers_count)
print $" ($count)"
[[shell repo stars]; [($r.name) ($r.repo) ($count)]]
}
} | flatten | sort-by -r stars | table --index 1

View File

@@ -0,0 +1,28 @@
#!/usr/bin/env nu
#script to get basic info from twitter's unofficial API
def main [...usernames: string] {
let bearer = "Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA"
let token_endpoint = 'https://api.twitter.com/1.1/guest/activate.json'
let user_endpoint = 'https://twitter.com/i/api/graphql/gr8Lk09afdgWo7NvzP89iQ/UserByScreenName'
#obtaining the guest token needed to perform further request
let token = (
post -H [Authorization $bearer] $token_endpoint ''
).guest_token
for $twitter_username in $usernames {
#getting all the useful data from the api
let variables = {
screen_name: $twitter_username,
withSafetyModeUserFields: true,
withSuperFollowsUserFields: true
}
post $user_endpoint -t application/x-www-form-urlencoded [ variables ($variables|to json -r) ] -H [ Authorization $bearer, x-guest-token $token ] | get data.user.result | flatten | select name screen_name description protected verified created_at followers_count rest_id has_nft_avatar | get 0
}
}