open foo.txt | append "world"| save --raw foo.txt
Oh. open foo.txt | append "world"| save --raw foo.txt
Oh.I love it!
It may seem a bit odd or even cumbersome to experienced shell programmers, but in my experience with Fish, this kind of thing helps a lot with ease of learning.
Fish is amazing!
I.e., there is no graceful scaling path.
I wonder how these other shells work with the backticks and such in Perl/Ruby. Theoretically there is room for very cool interoperability but I wonder how it goes in practice and if any nice tools and patterns are being developed.
And there's no hard constraint that would require this to "break down at some point". A well designed shell could also be a "real scripting language", all-in-one.
But the horrible CP/M and POSIX legacy of shell design doesn't help with that.
There are some old job control bugs, job control behaviors that used to not be POSIX compliant, and one outstanding bug having to do with IO redirection in fish functions that seem like they could be sort of relevant here. But there's nothing I can think of that I could interpret as 'lacking subprocesses'.
I only spent a few minutes looking into this, though. I'm curious to know if you can still recall more specifics, so many years later! Maybe there's something from before I started using fish that I've missed here. But fish is only 18 years old, going by the date of its first release, and I've been using it as a daily driver for ~13 years, and it has had everything I might call 'subprocesses' for that entire duration.
cat somefile.txt | whatever
and instead tell them to use either whatever < somefile.txt
or, if the command accepts input file names as arguments then use those like whatever somefile.txt
or whatever -i somefile.txt
etcBut perhaps in fish, “useless use of cat” is not so useless and would be recommended?
(I mean aside from the fact that they seem to recommend their “open” command, and so they probably prefer their own “open” over “cat”.)
But most of all the cat way just aligns with my mental model more. Data flows left to right, if you catch my drift.
It also makes it easier to add arguments to the end if re-running it.
> It also makes it easier to add arguments to the end if re-running it.
I'd like to point out that a redirection doesn't have to be the last thing in a command. E.g. the common 'echo >&2 "some message"' works fine.
Using `<` doesn't change that model. You can write `< somefile.txt whatever`. I always write my commandlines as `<in_file cmd1 | cmd2 | cmd3 >out_file`
(Though annoyingly this doesn't work for feeding input to while loops. `< <(echo a; echo b; echo c;) while read -r foo; do echo "$foo"; done` is invalid syntax; it needs to be `while read -r foo; do echo "$foo"; done < <(echo a; echo b; echo c;)`)
If you showed that without context it would look like some prefix/lispy notation so no, you are just showing how bad it is
random-app ./myfile.txt
The app opens the file on its own. It could decide to write to it. cat ./myfile.txt | random-app
The app receives chunks of the file from a pipe. It doesn't know where the original file is.This is especially useful if you aren't sure the program will by default modify the file (such as formatters).
random-app < ./myfile.txt $ echo foo > a.txt
$ <a.txt ( echo bar >/proc/self/fd/0 )
$ cat a.txt
barSo, hiding the pathname from an untrusted tool via an unnamed pipe is a Posix security measure. Still bizarre!
Who was the guy who awarded UUOC prizes? Wasn't he a real dyed-in-the-wool security wonk?
There's no reason IMHO to avoid using a file as an argument, or directly as stdin. If you don't trust an app, don't run it in your user account; you run it in a sandbox, right? This is 2023.
Now a case could be made for defending against misbehavior by an app that might write to an fd by mistake, but as a1369209993 demonstrates, writing to stdin is a very deliberate choice, as you'll need to look up a pathname and deliberately open that file as writable. That's not misbehavior, that's malice, and that doesn't belong anywhere near your user account in the first place.
> When I offer a pipeline as a solution I expect it to be reusable. It is quite likely that a pipeline would be added at the end of or spliced into another pipeline. In that case having a file argument to grep screws up reusability, and quite possibly do so silently without an error message if the file argument exists. I. e. `grep foo xyz | grep bar xyz | wc` will give you how many lines in xyz contain bar while you are expecting the number of lines that contain both foo and bar. Having to change arguments to a command in a pipeline before using it is prone to errors. Add to it the possibility of silent failures and it becomes a particularly insidious practice.
For no good reason at all.
It's not less confusing, and when shell programmers start talking about how wasteful it is to fork another process, it's because all the good explanations were already refuted.
Also, it's worth pointing that this style was never unanimous. There is a very vocal minority that pushes for it, and a wide non-vocal majority that says basically "whatever, it's not like there's any difference" if you go and ask the question.
But how often do you make those kinds of changes in any of your scripts and not have to change anything? For me, exactly 0 times
$ < file util1 | util2 | util3 | ...
The first util is the originator of the data pipeline, and it plus the file are the first command $ cat file | util1 | ...
Each utility is it's own thing, being fed a data stream that came from cat. I know it's just personal preference but it feels neater open foo.txt out> bar.txtI think this might hit the sweetspot for me, for dealing with json - I never could get a feel for "jq" like I have with sed/awk, and don't much enjoy powershell.
I don't think I need a new shell (bash is fine - and I would probably move to fish for a new shell if I were to change) - but I certainly need a sane way to wrangle json (hello docker inspect!) and yaml (hello kubectl/k8s and github actions!).
The great thing about shell languages is that you get to practice the scripting language with your everyday interactive usage, which is one of the main reasons I'd eventually like to switch, even if I see that perhaps the most exciting use cases are wrangling JSON and YAML in scripts.