Skip to content

proposal: {var}>filename redirection #12547

Description

@Nahor

This relates to issues #12542, #3948 and #3303 (and possibly others)

There is a general issue that fish cannot easily control what file descriptors get used and when (some external crates create their own fds for instance). This can create some issues when a script tries to use the same file descriptor value. and I believe it's also what prevent fish from implementing feature such as exec n>file.

So I think using the {var}>filename Bash feature would solve all those problems, especially if we make that the only way to use fds >2.

For reference, {var}>filename in Bash allows the shell to choose what file descriptor to use and stores the result in var. Then one can use it in other commands (e.g. cat <&$var).

fish could have such a feature, and possibly make that the only way to use file descriptors other than 0, 1 and 2 to avoid further issues (assuming only very few and/or niche apps require the shell to setup file descriptors at specific values).

Details:

  • in place of exec, implement a set --fd var filename (potentially, it could be a new set_fd builtin, but there is a lot of commonalities between set and would-be set_fd)
    • possibly, set --fd would setup a special kind of variable (i.e. different from the actual "string" type) for extra features/safety (i.e. prevent one from using a string where an fd is expected, see following points)
      • alternatively, and if using set_fd, the descriptors variables could be a completely separate set of variables, but I feel this would create another set of issues (e.g. how to refer to such variables since $var is for string?)
    • deleting/reassigning the variable would close the fd
      • this would allow using the variable scope (set -[ugfl]) to auto-close the fd, without having the need for an explicit exec n>&- equivalent.
        • this would reduce the risk of fd leaks (repeated assignment without explicit closing)
        • increase safety (non-exported variables would close their fds before spawning a new process)
    • an existing fd variable can only be assigned a new filename and other another fd variable (fd duplication)
      • this eliminates the risk of assigning some random not-set-by-user fd (no set --fd my_fd 3)
    • the value of an fd could either be just the file descriptor value $fd == 10, or for convenience/debug/readability be an array of [fd_value, filename] (i.e. $fd[1] == 10, $fd[2] == <filename>).
    • set --fd could support extra options, like --perm 0640, --mode [ro,wo,rw], --append, --no-clobber, ...
  • implement cmd {var}>filename for inline redirection
    • this would be equivalent to begin; set -lx var filename; cmd; end
    • because of the equivalency, echo $var {var}>finename should work (and print the fd value). So would /proc/self/fd/$var {var}</usr/bin/fish.
  • implement default variables for stdin/stdout/stderr for consistency, so one could do set --fd stdin my_file_to_read (i.e. exec 0<my_file_to_read)
    • this would also work in combination with set --fd var other_fd_var to duplicate the stdxxx descriptors (e.g. set --fd stderr stdout, aka 2>&1)
    • the filename for the default stdin/stdout/stderr would be its dev path (/dev/pts/x). Potentially, it could be - to be dev agnostic, but - only means current stdxxx, so its meaning/target changes when stdxxx changes
  • deprecate the use of n>file other than for 0,1 and 2.

Potential issues:

  • the begin; set -lx var filename; cmd; end equivalency:
    this mean setting up the redirections before the rest of the command line can be expanded (for info, bash doesn't support it, and one needs to use a block to workaround it, i.e. { cmd $var; } {var}<filename)
  • how to represent an "fd var" as an environment variable, i.e. for other apps?
    • also, that representation needs to be reversible so that, for example, fish > bash > fish should allow the child fish to recreate the parent fish fd vars (the fish > fish case is trivial if fish doesn't exec() itself after forking)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions