PIPE: Add pipe support for passing data into and out of terminal commands#2525
PIPE: Add pipe support for passing data into and out of terminal commands#2525ficocelliguy wants to merge 104 commits into
Conversation
… on string literals
…pts with piped arguments as input
… game is restarted
… react warning form updating terminal after a command that causes navigation
…cts.md Co-authored-by: David Walker <d0sboots@gmail.com>
…cts.md Co-authored-by: David Walker <d0sboots@gmail.com>
…cts.md Co-authored-by: David Walker <d0sboots@gmail.com>
…d in the terminal (excluding long-running processes)
…ner-src into pipe_support
…ntAndBypassPipes in places where the content isn't intended to be piped
…instead of Terminal.error
d0sboots
left a comment
There was a problem hiding this comment.
I'm gonna have to review the whole thing from the top aren't I 😭
|
|
||
| ``` | ||
| [home /]> echo test123 >> newFile.txt | ||
| [home /]> cat < myfile.txt | cat |
There was a problem hiding this comment.
I would use your grep example from above here, to show how < can be used to avoid needing a pipe.
grep "error" < myfile.txt
I get that you want to show the "only first" property, so maybe it can be "in addition to" instead of replacing.
|
|
||
| ### Semicolons: `;` | ||
|
|
||
| Semicolons let you run multiple independent commands on one line. Each command is separate — they do not share pipes or redirects. |
There was a problem hiding this comment.
I'd add something about sequencing here. Specifically, in Bitburner (unlike in unix), each command runs in parallel IIRC, and this is noticable if you try to do hack;hack.
|
|
||
| ### Piping into scripts | ||
|
|
||
| When a script is part of a pipe chain, it can read from stdin using `ns.getStdin()`. The script receives whatever the previous command wrote to stdout. |
There was a problem hiding this comment.
Just say "the terminal" instead of stdout, it's clearer in our case.
There was a problem hiding this comment.
"read from the terminal" is what current grep -p does, unlike getStdin. stdin isn't the visible terminal broadly, but only data piped in from an output redirect of some sort. I've re-worded this to try to improve the clarity.
| export function commonEditor(command: string, { args, server, vim }: EditorParameters, allowZeroFiles = false): void { | ||
| if (args.length < 1 && !allowZeroFiles) { | ||
| return Terminal.fatal(`Incorrect usage of ${command} command. Usage: ${command} [scriptname]`); | ||
| return Terminal.error(`Incorrect usage of ${command} command. Usage: ${command} [scriptname]`, getTerminalStdIO()); |
There was a problem hiding this comment.
What is your logic for turning these, and things like these, back to error?
There was a problem hiding this comment.
fatal explicitly closes the provided stdIO. For places where that didn't seem important, I used error. I can use whichever you think makes sense, though, for things like this.
| if (args.length !== 0) return Terminal.fatal("Incorrect usage of hack command. Usage: hack"); | ||
| if (server.purchasedByPlayer) return Terminal.fatal("Cannot hack your own machines!"); | ||
| if (!server.hasAdminRights) return Terminal.fatal("You do not have admin rights for this machine!"); | ||
| if (args.length !== 0) return Terminal.fatal("Incorrect usage of hack command. Usage: hack", stdIO); |
There was a problem hiding this comment.
I thought the whole point of fatal was that it didn't need this extra argument?
There was a problem hiding this comment.
fatal is used to close the StdIO, as opposed to error which just logs red text. There are only a handful of places where there isn't a StdIO already. More importantly, I kept running into places that missed passing the StdIO to fatal after the big merge conflict resolution, so it seemed more future-proof to require it.
| } | ||
| const handle = new PortHandle(stdinHandle.n); | ||
| // Provide only the methods the player needs to access (or write to) stdio | ||
| return { |
There was a problem hiding this comment.
This creates a new set of closures for every handle. We definitely want to be returning a class instead. If PortHandle isn't that class for some reason, then we should make a new one - but my impression was that PortHandle was that class.
There was a problem hiding this comment.
PortHandle is a class. I did this to avoid exposing the port number (which are not valid for player use cases.) if a player did something like ns.peek(ns.getStdin().n) it would throw an error about port numbers needing to be positive.
I could make a new class to do something similar, exposing only the relevant methods? or maybe I can just ignore this odd use case?

A number of players have asked for support for pipes in the terminal, or have been confused as to its lack of support:
https://discord.com/channels/415207508303544321/415207839246581781/1411811521935315016
https://discord.com/channels/415207508303544321/415207839246581781/1275557408726323260
https://discord.com/channels/415207508303544321/415207508303544323/932487013922390016
https://discord.com/channels/415207508303544321/415207508303544323/933463698431967283
https://discord.com/channels/415207508303544321/415207508303544323/573291980801703946
So, I've added support for them!
Deployed and testable at:
https://ficocelliguy.github.io/bitburner-src-2/
Features (and test cases):
echocommand added>is used, otherwise will append (>>)stdinadded: it is a port handle that receives any data passed to the script via pipes