New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
<?
try-input redirection
#10387
<?
try-input redirection
#10387
Conversation
d8bb2e2
to
de1e9ed
Compare
This tries to open the given file to use as stdin, and if it fails, for any reason, it uses /dev/null instead. This is useful in cases where we would otherwise do either of these: ```fish test -r /path/to/file and string match foo < /path/to/file cat /path/to/file 2>/dev/null | string match foo ``` This both makes it nicer and shorter, *and* helps with TOCTTOU - what if the file is removed/changed after the check? The reason for reading /dev/null instead of a closed fd is that a closed fd will often cause an error. In case opening /dev/null fails, it still skips the command. That's really a last resort for when the operating system has turned out to be a platypus and not a unix. Fixes fish-shell#4865
This is mostly used instead of `test -r foo && string match bar <foo` or `set bar (cat foo 2>/dev/null)`. In doing so it can avoid a weird race condition where the file changes between the check and the use, and it can avoid an external process or having to open the file twice. However it can also cause more work to happen - if you do ```fish if test -r file # do a lot of work with file end ``` it might be prudent to keep doing that. In the cases here it's usually just a few `string` calls, which would operate on nothing and so be pretty quick.
I like this in principle but I don't think we should just add this without taking some time to revisit fish scripting overall and put together our ideas in one place and see what sticks and what doesn't. This is a fairly big change (in fish philosophy, not code) and there are "symbol soup" concerns that we have thus far avoided (and even lobotomized out). The addition of various builtins and subcommands was fine; they're confined to the command itself, they're easy to look up, no one has to use them, and they're fairly self-documenting. In my two cents, changes to the language should be evaluated a lot more carefully. I think this should be part of a careful, methodical, and holistic review of fish scripting and not something we should just adopt on an ad-hoc basis. |
The big thing here is that we have redirections as syntax - there is no So there is no way to attempt a redirection that can be caught or handled without printing an error (you couldn't really redirect the error anyway). The alternatives are pretty verbose, subtly wrong ( That's why I think that this addition is necessary, and why there is no good alternative using a builtin. We also already use the exact symbol with redirections In general I'm a fan of rethinking fish script, and I'm not a fan of adding symbols. Fish's original design was extremely ad-hoc and sometimes incoherent. That's why it had But here, where we already have the symbols, I think reusing one as a modifier makes sense. |
That sounds like a good solution, it's simple and obvious. What's wrong with the external command? I haven't formed a strong opinion on If I had to decide now I'd say it's probably fine to add because it's hardly ambiguous. |
It requires calling an external process, it's pretty verbose. We could also get by without
That's a command substitution. It is absolutely not the same thing. It also can not be used for this, because it will print an error if the file does not exist, and the error is unredirectable.
That one is quite literally just a shortcut, and since it syntactically means you need to allow redirections without commands I do not believe that change is a good idea. It saves three characters at the cost of making the syntax more complex and making errors less detectable - now a missing command is no longer an error if there's a redirection. (in zsh |
ok. I'm more likely to use |
I mean in general unix-style shellscripting has unavoidable TOCTTOUs. It is essentially impossible to avoid. But here, it is a fix for this specific one - once the file is open it's open. Longer term, I would like to have a thing to open a file at a file descriptor. I would not want to reuse redirect -l file --write /path/to/file # redirect sets $file to the fd, it is closed once $file goes out of scope
foo >$file
redirect --stdout /path/to/file # anything going to stdout now goes to /path/to/file |
Merged as 2024313. (like I said, the second commit was for illustration and was not merged) |
Description
This tries to open the given file to use as stdin, and if it fails, for any reason, it uses /dev/null instead.
This is useful in cases where we would otherwise do either of these:
This both makes it nicer and shorter, and helps with TOCTTOU - what if the file is removed/changed after the check?
The reason for reading /dev/null instead of a closed fd is that a closed fd will often cause an error.
In case opening /dev/null fails, it still skips the command. That's really a last resort for when the operating system has turned out to be a platypus and not a unix.
Fixes #4865
The main issue here, as discussed in 4865, is that this is arguably inconsistent with
>?
redirection - where that errors out,<?
does not error out. Unfortunately the only solution I can come up with is to make<
noclobber and make<?
clobber, which also doesn't really seem right and would be an awkward change.<?
also currently cannot be combined with file descriptors -<?&3
errors out. This could be lifted in future ("use this file descriptor if it is open"), tbh I'm fine with this at the moment because we don't have a way to globally open file descriptors anyway so you could really only use stdin, and that's pretty much always open.TODOs:
Note: The second commit is for illustration, I'm not going to merge it because it's not really necessary and annoying for the people who want new scripts on old fish (which we don't "support" as such, but try not to gratuitously break).