fish-shell/tests/checks/path.fish
Fabian Homborg f6fb347d98 Add "path" builtin
This adds a "path" builtin that can handle paths.

Implemented so far:

- "path filter PATHS", filters paths according to existence and optionally type and permissions
- "path base" and "path dir", run basename and dirname, respectively
- "path extension PATHS", prints the extension, if any
- "path strip-extension", prints the path without the extension
- "path normalize PATHS", normalizes paths - removing "/./" components
- and such.
- "path real", does realpath - i.e. normalizing *and* link resolution.

Some of these - base, dir, {strip-,}extension and normalize operate on the paths only as strings, so they handle nonexistent paths. filter and real ignore any nonexistent paths.

All output is split explicitly, so paths with newlines in them are
handled correctly. Alternatively, all subcommands have a "--null-input"/"-z" and "--null-output"/"-Z" option to handle null-terminated input and create null-terminated output. So

    find . -print0 | path base -z

prints the basename of all files in the current directory,
recursively.

With "-Z" it also prints it null-separated.

(if stdout is going to a command substitution, we probably want to
skip this)

All subcommands also have a "-q"/"--quiet" flag that tells them to skip output. They return true "when something happened". For match/filter that's when a file passed, for "base"/"dir"/"extension"/"strip-extension" that's when something about the path *changed*.

Filtering
---------

`filter` supports all the file*types* `test` has - "dir", "file", "link", "block"..., as well as the permissions - "read", "write", "exec" and things like "suid".

It is missing the tty check and the check for the file being non-empty. The former is best done via `isatty`, the latter I don't think I've ever seen used.

There currently is no way to only get "real" files, i.e. ignore links pointing to files.

Examples
--------

> path real /bin///sh
/usr/bin/bash

> path extension foo.mp4
mp4

> path extension ~/.config
  (nothing, because ".config" isn't an extension.)
2022-05-29 17:48:11 +02:00

108 lines
2.5 KiB
Fish

#RUN: %fish %s
# The "path" builtin for dealing with paths
# Extension - for figuring out the file extension of a given path.
path extension /
or echo None
# CHECK: None
# No extension
path extension /.
or echo Filename is just a dot, no extension
# CHECK: Filename is just a dot, no extension
# No extension - ".foo" is the filename
path extension /.foo
or echo None again
# CHECK: None again
path extension /foo
or echo None once more
# CHECK: None once more
path extension /foo.txt
and echo Success
# CHECK: txt
# CHECK: Success
path extension /foo.txt/bar
or echo Not even here
# CHECK: Not even here
path extension . ..
or echo No extension
# CHECK: No extension
path extension ./foo.mp4
# CHECK: mp4
path extension ../banana
# nothing, status 1
echo $status
# CHECK: 1
path extension ~/.config
# nothing, status 1
echo $status
# CHECK: 1
path extension ~/.config.d
# CHECK: d
path extension ~/.config.
echo $status
# one empty line, status 0
# CHECK:
# CHECK: 0
path strip-extension ./foo.mp4
# CHECK: ./foo
path strip-extension ../banana
# CHECK: ../banana
# but status 1, because there was no extension.
echo $status
# CHECK: 1
path strip-extension ~/.config
# CHECK: {{.*}}/.config
echo $status
# CHECK: 1
path base ./foo.mp4
# CHECK: foo.mp4
path base ../banana
# CHECK: banana
path base /usr/bin/
# CHECK: bin
path dir ./foo.mp4
# CHECK: .
path base ../banana
# CHECK: banana
path base /usr/bin/
# CHECK: bin
cd $TMPDIR
mkdir -p bin
touch bin/{bash,bssh,chsh,dash,fish,slsh,ssh,zsh}
ln -s $TMPDIR/bin/bash bin/sh
chmod +x bin/*
# We need files from here on
path filter bin argagagji
# The (hopefully) nonexistent argagagji is filtered implicitly:
# CHECK: bin
path filter --type file bin bin/fish
# Only fish is a file
# CHECK: bin/fish
chmod 500 bin/fish
path filter --type file,dir --perm exec,write bin/fish .
# fish is a file, which passes, and executable, which passes,
# but not writable, which fails.
#
# . is a directory and both writable and executable, typically.
# So it passes.
# CHECK: .
path normalize /usr/bin//../../etc/fish
# The "//" is squashed and the ".." components neutralize the components before
# CHECK: /etc/fish
path normalize /bin//bash
# The "//" is squashed, but /bin isn't resolved even if your system links it to /usr/bin.
# CHECK: /bin/bash
# We need to remove the rest of the path because we have no idea what its value looks like.
path real bin//sh | string match -r -- 'bin/bash$'
# The "//" is squashed, and the symlink is resolved.
# sh here is bash
# CHECK: bin/bash