Useful fish functions
Add functions/fast-sync-repos.fish
Make init-sourcehut use new function add-sourcehut-remote
Make unsymlink completion faster and simpler


browse  log 



You can also use your local clone with git send-email.

Functions for the Fish Shell, making common tasks more convenient.


Backup any existing ~/.config/fish, then:

git clone https://git.sr.ht/~razzi/fish-functions ~/.config/fish

In previous versions, other fish config including abbrs were included as well. That changed much more frequently than the functions, so I split them out.

Now they are at https://git.sr.ht/~razzi/dotfiles (see that repository's README for installation instructions).


#File Manipulation

#backup <file> (source)

Creates a copy of file as file.bak.

$ ls
$ backup README.md
$ ls
README.md  README.md.bak

Recommended abbreviation: abbr -a bk backup

#restore <backup> (source)

Rename a backup such as file.bak to remove the .bak extension.

$ ls
$ restore README.md.bak
$ ls

Recommended abbreviation: abbr -a re restore

#mkdir-cd <directory> (source)

Make a directory and cd into it.

$ mkdir-cd folder
folder $

Recommended abbreviation: abbr -a mc mkdir-cd

#copy <source> ... [<destination>] (source)

cp with some extra behaviors.

Automatic recursive copy for directories. Rather than only copying the files from a directory, copies the directory itself.

Also uses -i flag by default, which will warn you if a copy would overwrite a destination file.


$ mkdir testdir
$ touch testdir/file.txt
$ mkdir destdir

# Standard cp needs -r flag
$ cp testdir/ destdir/
cp: testdir/ is a directory (not copied).

# And does not preserve the source folder
$ cp -r testdir/ destdir/
$ ls destdir/

# Cleaning up...
$ rm destdir/file.txt

# In contrast, using `copy` function:
$ copy testdir/ destdir/
$ ls destdir/

Recommended abbreviation: abbr -a cp copy. If you do this abbreviation, use command cp for the low-level cp.

#create-file <target> (source)

Creates a file, including parent directories as necessary.

$ create-file a/b/c
$ tree
└── a
    └── b
        └── c

#eat <target> (source)

Moves a directory's contents to the current directory and removes the empty directory.

$ tree
└── a
    └── b
        └── c
$ eat a
$ tree
└── b
    └── c

If a file in the current directory would be overwritten by eat, it will error with exit status 1.

An illustration of this:

$ tree
├── dir-a
│   └── dir-b
│       └── some_file
└── dir-b
    └── would_be_overwritten

3 directories, 3 files
$ eat dir-a
eat: file would be overwritten: ./dir-b

#move <source> ... <destination> (source)

Like mv but uses -i flag by default, which will warn you if mv would overwrite a destination file.

Also warns you if you are trying to move a directory symlink which is ending in slash:

$ mkdir mydir
$ ln -s mydir mylink
$ mv mylink/ renamed
mv: cannot move 'mylink/' to 'renamed': Not a directory

move gives a more descriptive error:

$ move mylink/ renamed
move: `from` argument "mylink/" is a symlink with a trailing slash.
move: to rename a symlink, remove the trailing slash from the argument.

This arises because tab completion adds the slash. Completion for move could avoid the slash, but then again you might want to move a file within the symlinked directory.

#remove <target> (source)

rm with an extra behavior.

If removing a directory with write-protected .git, confirm once to ensure the git directory is desired to be removed.

$ ls -a dodo
.  ..  .git  x
$ remove dodo
Remove .git directory dodo/.git?> y

Using plain rm:

$ rm -r dodo
override r--r--r--  razzi/staff for dodo/.git/objects/58/05b676e247eb9a8046ad0c4d249cd2fb2513df? y
override r--r--r--  razzi/staff for dodo/.git/objects/f3/7f81fa1f16e78ac451e2d9ce42eab8933bd99f? y
override r--r--r--  razzi/staff for dodo/.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391? ^C
$ rm -rf dodo

Recommended abbreviation: abbr -a rm remove. If you do this abbreviation, use command rm for the low-level rm.

#move-last-download [<dest>] (source)

Move the latest download to destination directory, which is the current directory if none is specified.

Recommended abbreviation: abbr -a mvl move-last-download.

#Zipfile Utilities

#clean-unzip <zipfile> (source)

Unzips a .zip archive without polluting the current directory, by creating a directory even if the zipfile does not have a folder level.

#unzip-cd <zipfile> (source)

Unzip a zip directory and cd into it. Uses clean-unzip to create a folder if the zipfile doesn't have one.

$ unzip-cd files.zip
Archive:  files.zip
 extracting: out/a.txt
 extracting: out/b.txt
files $ ls
a.txt  b.txt

#Text Utilities

#coln <number> (source)

Splits input on whitespace and prints the column indicated.

$ echo 1 2 | coln 2

#row <number> (source)

Prints the row of input indicated.

$ seq 3 | row 2

#skip-lines <number> (source)

Skips the first n lines of stdin.

$ seq 5 | skip-lines 2

#take <n> (source)

Take the first n lines of standard input.

$ seq 5 | take 3

#word-count (source)

Count the words from standard input. Like wc -w but does not put spaces around the number.

$ echo a b | word-count
# Compare to:
$ echo a b | wc -w

#line-count (source)

Count the lines from standard input. Like wc -l but does not put spaces around the number.

$ seq 3 | line-count
# Compare to:
$ seq 3 | wc -l

#char-count (source)

Count the characters from standard input. Like wc -c but does not put spaces around the number.

$ echo -n a b | char-count
# Compare to:
$ echo -n a b | wc -c

#fish utilities

#string-empty <value> (source)

Test if the value is the empty string.

$ string-empty ''
$ echo $status

Can be used to test for arguments:

$ function something
    if string-empty $argv
        echo No arguments passed
        echo Arguments were passed
$ something
No arguments passed
$ something 1
Arguments were passed

If you use this on a variable, be sure to get the variable's value using $:

$ if string-empty $VIRTUAL_ENV
    echo in venv

since string-empty VIRTUAL_ENV will always return false.

#file-exists <file> (source)

Test if $file exists.

#is-dir <path> (source)

Check if if $path is a directory.

Check if if $path is a symlink.

#funcsave-last (source)

Save the last-edited fish function.

$ function hi
  echo hi
$ funcsave-last
Saved hi

Recommended abbreviation: abbr -a fs funcsave-last.

#confirm (source)

Prompts the user for confirmation. Exit with status according to whether they answered y, Y, yes, or YES.

#Environment Utilities

#curdir (source)

Just the current directory name, please.

mydir $ curdir

You probably won't need this interactively since the current directory is usually part of your fish_prompt, but this is useful for scripting.

#echo-variable <variable> (source)

Like echo, but without the $ or capitalization.

$ echo-variable user
$ echo $USER

Recommended abbreviation: abbr -a ev echo-variable.

Completion: completes environment variable names.

#readpass <name> (source)

Prompt for a password. Does not echo entered characters.

$ readpass email
$ echo $email

Create a symbolic link, using absolute paths.

~/dotfiles $ symlink .prettierrc ~
~/dotfiles $ cat ~/.prettierrc
singleQuote: true
semi: false

Without using absolute paths:

~/dotfiles $ ln -s .prettierrc ~
~/dotfiles $ cat ~/.prettierrc
cat: /Users/razzi/.prettierrc: Too many levels of symbolic links

Remove a symlink. Errors if the file is not a symlink.

List symlinks in the given directory, or the current directory if none is passed.

Create a symlink from $file to the home directory (~).

#git utilities

#clone-cd url [destination] (source)

Clone a git repository into the current directory (or the optional $destination), and cd into it. Clones with depth 1 for speed.

If a folder by that name already exists, great, you probably already cloned it, just cd into the directory and pull.

If it's trying to clone into a non-empty directory, make a new folder in that directory with the repository name and clone into that, instead of erroring.

#wip [message] (source)

Adds untracked changes and commits them with a WIP message. Additional arguments are added to the WIP message.

I use this instead of git stash so that changes are associated with the branch they're on, and the commit is tracked in the reflog.

$ git stat
## master
M      tests.py
$ git switch -c testing
$ wip failing tests
[testing 0078f7f] WIP failing tests
$ git switch -

#git-add [paths] (source)

Like git add, but defaults to . if no arguments given, rather than erroring.

Also understand ... to mean ../... If you need more levels of ../.. I guess they could be added.

Did I mention I have a function called ... that cds up 2 levels?

Recommended abbreviation: abbr -a ga git-add

#git-commit [message] (source)

Like git commit -m without the need to quote the commit message.

If no commit message is given and there's only 1 file changed, commit "(Add / Update / Delete) (that file)".

$ git-commit
[master c77868d] Update README.md
 1 file changed, 57 insertions(+), 18 deletions(-)
$ git reset @^
Unstaged changes after reset:
M       README.md
$ git-add
$ git-commit Fix typo in README.md
[master 0078f7f] Fix typo in README.md
1 file changed, 57 insertions(+), 18 deletions(-)

Recommended abbreviation: abbr -a gc git-commit

#gitignore <pattern> (source)

Add a pattern to the .gitignore.

Recommended abbreviation: abbr -a giti gitignore.

#Vim Utilities

#vim-plugin <url> (source)

Install a vim plugin using the builtin vim plugin mechanism.

#Postgres Utilities

#ensuredb <name> (source)

Ensure that a fresh database by the name given is created. Drops a database by that name if it exists, clearing database connections as necessary.

#renamedb <from> <to> (source)

Renames a database.

#Date Utilities

#isodate (source)

Prints the date in ISO format.

$ isodate

#MacOS Utilities

#wifi-network-name (source)

Prints the current wifi network name.

#wifi-password (source)

Prints the current wifi network password.

#wifi-reset (source)

Turns the wifi off and on again.