News for hackers. Stuff that matters.
Re:Command Line Interface Guidelines by pimlottc on Tuesday February 06, 2024 @10:32AM (#39275348)
Please also consider a --dry-run option that gives a preview of what actions would be taken without actually making any changes. This is really helpful for learning a tool and making sure you got complex options and fileglobs correct before committing to potentially irreversible changes.
Re:Command Line Interface Guidelines by epage on Tuesday February 06, 2024 @12:36PM (#39277535)
Or if the command can't be undone and with significant side effects, be `--dry-run` by default and have an `--execute` flag
Re:Command Line Interface Guidelines by BD103 on Tuesday February 06, 2024 @01:50PM (#39278693)
Exactly. This is implemented by the Javascript code formatter Prettier [1] where you have to pass `--write` in order to overwrite an unformatted file.

[1]: https://prettier.io

Re:Command Line Interface Guidelines by samstave on Tuesday February 06, 2024 @10:33AM (#39275377)
dry-run should be a pipable | function for any command;

> do_thing.py | dry-run

--

Think of it as "explain your work, step by step" as one would prompt...

also, its a food-for-thought you muppets.

---

@jasonjmcghee ; ( $ ) . ( $ ) great justice.

Re:Command Line Interface Guidelines by jasonjmcghee on Tuesday February 06, 2024 @10:37AM (#39275433)
How could that work? The script on the left executes first, piping its output to another command won’t prevent that
Re:Command Line Interface Guidelines by saghm on Tuesday February 06, 2024 @11:12AM (#39276029)
I think there are ways to detect if stdout is a pipe and operate differently (e.g. by using different defaults for coloring output), but I'm not sure if there are ways to detect what the other side of the pipe is, much less what `dry-run` would actually be expected to be in this case
Re:Command Line Interface Guidelines by jasonjmcghee on Tuesday February 06, 2024 @11:31AM (#39276377)
Maybe I’m misunderstanding but sounds like you’re proposing the modifying the command on the left to detect whether it’s piping it’s output (curious how you do that btw sounds pretty cool / useful)

But at that point you could just handle —dry-run directly

Re:Command Line Interface Guidelines by kergonath on Tuesday February 06, 2024 @11:38AM (#39276497)
> Maybe I’m misunderstanding but sounds like you’re proposing the modifying the command on the left to detect whether it’s piping it’s output (curious how you do that btw sounds pretty cool / useful)

You can do that. Possibly not from all languages but for anything that can call functions in the c standard library, that’s what isatty() is for (among other uses). It takes a file descriptor and returns whether it is a terminal or not. If you do this with stdout, this tells you whether it goes to a terminal or whether it is redirected in a way.

As the parent suspects, though, this won’t tell you anything about what is on the other side of the redirection.

Re:Command Line Interface Guidelines by jasonjmcghee on Tuesday February 06, 2024 @11:45AM (#39276632)
Very cool function - thank you!
Re:Command Line Interface Guidelines by epcoa on Wednesday February 07, 2024 @08:38AM (#39288378)
It also doesn’t tell you what is the terminus of a pipeline. Because often with isatty what you really want to know is if the pipeline ends with a tty. `ls` is screen formatted but `ls | less` is not without extra work.
Re:Command Line Interface Guidelines by gpderetta on Tuesday February 06, 2024 @12:00PM (#39276912)
on the other hand you could have 'dry-run <command>' that via .so interposition tricks could intercept and list all destructive changes done by an arbitrary <command>, as a form of sandboxing.
Re:Command Line Interface Guidelines by account42 on Wednesday February 07, 2024 @09:22AM (#39288886)
Not generally because you can't e.g. know if a write to a socket is destructive or just a query for information needed to decide the next steps/output.
Re:Command Line Interface Guidelines by gpderetta on Wednesday February 07, 2024 @10:27AM (#39289661)
True, I was only thinking of fs access!
Re:Command Line Interface Guidelines by kjs3 on Tuesday February 06, 2024 @11:44AM (#39276614)
If it operated differently based on what it was outputting to, that kinda defeats the point of 'dry run', which is to see exactly what's going to happen based on what's on the other side of the pipe when I run it for real. "Did this blow up because it's a bad command, or because there's a bug in the 'only kinda real dry run' code?".
Re:Command Line Interface Guidelines by samstave on Tuesday February 06, 2024 @04:10PM (#39280660)
+++

-

What if there wasa 'deep-pipe' '||' which would be based on a set env/docker/blah - which would launch an env and execute your '||'d code in it, and output some log/metrics/whatever?

Re:Command Line Interface Guidelines by sokoloff on Tuesday February 06, 2024 @12:37PM (#39277556)
I often pipe output to tee and would be pretty annoyed if that changed the behavior of the original command to not do anything because stdout was a pipe.
Re:Command Line Interface Guidelines by indymike on Tuesday February 06, 2024 @03:49PM (#39280370)
> I often pipe output to tee and would be pretty annoyed if that changed the behavior of the original command

The output sent to tee is usually not the same as the output from the command to the terminal, so you are getting something different than most human users expect from original command... the reason is that terminal escape codes and other formatting for humans may need to be omitted from output to a pipe. You do this by asking the OS, "is this thing a terminal?".

Python example

"terminal" if sys.stdout.isatty() else "something else"

C is very similar:

if (isatty (1)) fprintf (stdout, "Terminal."); else fprintf (stdout, "Not Terminal.");

(printf works, too).

Re:Command Line Interface Guidelines by sokoloff on Tuesday February 06, 2024 @03:57PM (#39280474)
Sure; the output format changes, but the functionality doesn't.

Even ls outputs a tabular format by default when it's on a terminal and a list one file/dir per line when it's not on a terminal (if it's piped to cat for example or why ls | wc -l correctly counts the entries).

But the (essential) behavior of the command remains the same. ls still lists files/dirs... scp still copies files, etc.

Re:Command Line Interface Guidelines by indymike on Wednesday February 07, 2024 @02:17PM (#39292912)
> But the (essential) behavior of the command remains the same.

Of course. A command needs to do it's defined function.

You'll find some programs that are quite a bit different when invoked from outside the terminal vs inside the terminal. Developers need to take into account both situations, which is really the point the original post.

Re:Command Line Interface Guidelines by saghm on Tuesday February 06, 2024 @07:43PM (#39283025)
You can see this pretty easily with `ls` as well by using different options for the color. If you run `ls --color=auto`, you'll get colored output when running directly but black and white output if you pipe to `less`. However, if you pass `--color=always`, you'll get colored output when running directly and a bunch of garbage around some of the entries when piping to `less` because it doesn't interpret ANSI escape codes for color by default (although depending on what the output you're piping into `less` is, there are some workarounds like https://www.gnu.org/software/src-highlite/)
Re:Command Line Interface Guidelines by funcDropShadow on Wednesday February 07, 2024 @06:57AM (#39287459)
You can use ls --color=always | less -R to render the colors in less.
Re:Command Line Interface Guidelines by samstave on Wednesday February 07, 2024 @08:08AM (#39288083)
Thats fn cool
Re:Command Line Interface Guidelines by saghm on Friday February 09, 2024 @08:21PM (#39322540)
Huh, not sure how I've never found that option before. Thanks for the lesson, I'll need to rtfm a bit more closely!
Re:Command Line Interface Guidelines by int_19h on Tuesday February 06, 2024 @01:55PM (#39278770)
Here's a possibly more interesting take on this: instead of do-thing.py, imagine if there was thing.py, which processed all that complex CLI options, and as output produced a linear shell script, with each line invoking some atomic operation, and a comment explaining which options contributed to that particular line.
Re:Command Line Interface Guidelines by eichin on Tuesday February 06, 2024 @02:47PM (#39279542)
Yeah, that's a pattern I use for occasional sysadmin tools - the command itself generates the actual commands, I review them, then "<up arrow> | sh -xeu". (Yes, there's no guarantee that the output is the same, so I don't use the pattern when that's a risk; it's also rarely if ever used for things I expect other people to run, just bulk operations that are saving me repetition.)
Re:Command Line Interface Guidelines by jasonjmcghee on Tuesday February 06, 2024 @11:39AM (#39276520)
Wasn’t trying to be a prick- i was commenting “here’s what I’m seeing with your proposal and the presented obstacles, how would you overcome them?”

One approach could be something like “set -x” after setting a confirmation with “trap” command.

    confirm_execution() {
        echo -n "Execute $BASH_COMMAND? [y/N] "
        read response
        if [[ $response != [yY] ]]; then
            echo "Skipped."
            return 1
        fi
    }

    trap 'confirm_execution' DEBUG
    set -x
    user_command
    set +x
But that’s a wrapper script
Re:Command Line Interface Guidelines by samstave on Tuesday February 06, 2024 @03:58PM (#39280502)
confirm_execution() { echo -n "Execute $BASH_COMMAND? [y/N] " read response if [[ $response != [yY] ]]; then echo "Skipped." return 1 fi }

draw_heart() { cat << EOF <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="100" height="100"> <path fill="red" d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/> </svg> EOF }

trap 'confirm_execution' DEBUG set -x draw_heart set +x

Re:Command Line Interface Guidelines by foobarqux on Tuesday February 06, 2024 @12:07PM (#39277005)
"try" is not a prefix (like watch, nice, etc) uses an overlayfs in order to be able to see and accept or reject changes to your filesystem from a command

https://github.com/binpash/try

Re:Command Line Interface Guidelines by koolba on Tuesday February 06, 2024 @10:38AM (#39275459)
Having it be a shell function would work. It could create a copy-on-write file system or override the syscalls for file system access.
Re:Command Line Interface Guidelines by agos on Tuesday February 06, 2024 @11:09AM (#39275984)
not all the destructive actions are on file system
Re:Command Line Interface Guidelines by bongodongobob on Tuesday February 06, 2024 @12:49PM (#39277726)
Whatif is one of my favorite things about powershell. Don't know what I'd do without it. Has saved me many times from completely destroying things.
Re:Command Line Interface Guidelines by ojintoad on Tuesday February 06, 2024 @01:16PM (#39278137)
Agree!

I'll go further. PowerShell covers a lot of the concerns in the OP out of the box. It is extremely well thought out and is one of my favorite cli models to buy into.

Re:Command Line Interface Guidelines by bigstrat2003 on Tuesday February 06, 2024 @02:42PM (#39279472)
Powershell is just plain a great shell. Blows all the *sh variants out of the water. I'd love for it to gain traction in Linux (so that, for example I could use it as my shell on my desktop) but I don't really see that happening.
Re:Command Line Interface Guidelines by partdavid on Wednesday February 07, 2024 @12:52PM (#39291705)
What are the things that are stopping you?

I use MacOS on my personal machine and Linux for various shellboxes and I switched to Powershell years ago and haven't looked back. Occasionally I invoke bash as a language runtime for checking shell script stuff, the way I would any other language's REPL, but for a shell? Powershell is strictly better.

The one actual problem with Powershell in this area is quoting for external commands. It's solvable in scripts by replacing certain things with double-quoted equivalents but not really interactively, and it is an occasional pain (though I still think it's overall less of a problem than quoting in general in bash and its ilk).

Re:Command Line Interface Guidelines by snuxoll on Tuesday February 06, 2024 @02:26PM (#39279241)
WhatIf in Powershell also remains completely broken; not that it doesn't work, but it is not properly passed down the call stack like it's supposed to from cmdlets or functions written in PowerShell (as opposed to those loaded from .net assemblies).
Re:Command Line Interface Guidelines by jcotton42 on Tuesday February 06, 2024 @02:50PM (#39279579)
Where have you seen this? If the function does it properly (SupportsShouldProcess) then it should pass down automatically.
Re:Command Line Interface Guidelines by partdavid on Wednesday February 07, 2024 @12:54PM (#39291731)
I have seen it, I think it's because it's not easy to see whether the cmdlets or functions your calling support and support it right. It does work, but I have seen it not always work and that results in having to check or test every command. I think the GP is overstating the incovenience, though, and Powershell is still much, much better than any Unix shell in this respect (which simply has no mechanism and no standard way to discover one).
Re:Command Line Interface Guidelines by snuxoll on Wednesday February 07, 2024 @04:02PM (#39294284)
I have written just in the last month cmdlets that have SupportsShouldProcess, and it does not pass on as it should. See https://learn.microsoft.com/en-us/powershell/scripting/learn... and https://github.com/PowerShell/PowerShell-RFC/pull/221#issuec...
Re:Command Line Interface Guidelines by jcotton42 on Thursday February 15, 2024 @02:23PM (#39387228)
Oh yikes!
Re:Command Line Interface Guidelines by danstewart_ on Tuesday February 06, 2024 @03:35PM (#39280199)
I tend to go the opposite way and have the default behaviour not actually make any changes and require passing `--commit` to actually do something.

I feel it’s safer for scripts that have irreversible (or difficult to reverse) actions.

Re:Command Line Interface Guidelines by enriquto on Tuesday February 06, 2024 @04:28PM (#39280898)
Even safer: your program does never perform any actual deed. It just prints commands that you can run afterwards, using an external program, ideally a shell. This has the advantage of allowing the user to edit all the actions one by one.

Instead of

    myprog           # see what would happen
    myprog --commit  # alright, do it

You do

    myprog           # see what would happen
    myprog | sh      # alright, do it

But if you want to change something:

    myprog > x
    vi x
    cat x | sh

And if you just want to run everything in parallel:

    myprog | parallel -j 16
Re:Command Line Interface Guidelines by azemetre on Tuesday February 06, 2024 @04:37PM (#39281010)
Do you have an example of a CLT that has a dry run flag? I am very confused on how to design one for some CLTs I'm making.

If you call an API to retrieve data, how can that be a dry run? Are you suppose to give fake examples with fake output?

Re:Command Line Interface Guidelines by scbrg on Tuesday February 06, 2024 @09:09PM (#39283618)
apt-get. It simply tells you what it's going to remove/install.

Calling an API to retrieve data is not really the type of program that requires a dry-run flag. It's mainly useful for commands that change the state of something in ways that could potentially be destructive, unwanted and/or hard to revert.

Re:Command Line Interface Guidelines by amake on Wednesday February 07, 2024 @12:25AM (#39284834)
The AWS CLI has --[no]-dry-run in many of its subcommands.

https://docs.aws.amazon.com/cli/latest/userguide/cli-usage-h...

Re:Command Line Interface Guidelines by funcDropShadow on Wednesday February 07, 2024 @06:53AM (#39287423)
make has -n or --dry-run. But originally it was called -n and that has been used many cli tools.