
Use long flags when scripting (2013) - maple3142
https://changelog.com/posts/use-long-flags-when-scripting
======
adrianmonk
Great suggestion, but I'd go for a middle ground: use long flags for the long
tail, but short flags are fine for stuff that's very commonly used.

I think I'll continue to write these:

    
    
        grep -i
        rm -rf
        ln -s
        gzip -v
        sed -e
    

instead of:

    
    
        grep --ignore-case
        rm --recursive --force
        ln --symbolic
        gzip --verbose
        sed --expression
    

If you're working on shell scripts, you probably know certain short flags well
enough that long flags just add clutter and don't improve readability.

~~~
OskarS
How often do you use gzip verbose? I don't think I've ever used it. What's it
good for?

~~~
zbuf
Personally a fan of '\--verbose' to be honest. It's clear. "grep -v" anyone?

~~~
frenchy
Particularly in this case, because `-v` sometimes means "print out the version
number".

~~~
jfrunyon
That's why they mentioned `grep -v`. Go look it up. It doesn't mean verbose OR
version.

Doubt you'd be printing out a version number in a shell script though.

~~~
cogburnd02
You may want to compare a version number of a command to make sure it's bigger
than (or equal to) the minimum version required by your script, though.

------
makecheck
It’s not just “easier for a human” but more _stable_ over time as commands
evolve, and less likely to do weird things across Unix variants.

Some command-line parsers will auto-complete partial options so you’re less
likely to see ambiguity errors in future versions if you picked long-form
options. (This isn’t completely foolproof, e.g. a command could have "\--foo"
and later add "\--foobar" but it does help in most cases.)

And unfortunately, some tools with the same name will use the same letter to
mean different things across Unix variants. You are asking for trouble if you
aren’t being clear about what you want.

~~~
zbuf
> Some command-line parsers will auto-complete partial options

This sort of behaviour is tortuous, and should be against international laws.

Where _adding_ new, unrelated, options to the interface now changes or breaks
the behaviour of existing calling scripts. Plus you never know which
abbreviations are in use in the wild.

It makes it virtually impossible to maintain a stable interface without just
freezing it long-hand.

I found this in Perl code; I believe it might be the default behaviour in the
standard parser? Our developer really did like the philosophy that "the user
may want 50 different ways to express the same thing".

------
jdmichal
This is how I write PowerShell scripts. I use full cmdlet names, full argument
switch names, and I even specify argument switches to positional arguments. I
also do my best to honor common flags like `-WhatIf`, `-Verbose`, and
`-ErrorAction`. So I end up with scripts like this:

    
    
        if (-not $(Test-Path -Path "$Path" -PathType Container)) {
            Write-Error -Message "Path ($Path) does not exist or is not a directory." -Category InvalidArgument;
            return;
        }
    

When you do this properly, it feels like magic. For example, I wrote a script
that does local Maven and Docker builds for a bunch of related projects. So I
wrote two functions `Build-Maven` and `Build-Docker` with proper common flag
support and error handling. Then, when I use them, I just do something like
this:

    
    
        $PSDefaultParameterValues = @{
            'Build-Maven:ErrorAction' = 'Stop';
            'Build-Docker:ErrorAction' = 'Stop';
        };
    
        Build-Maven "$Path\A";
    
        Build-Maven "$Path\B";
        Build-Docker -Path "$Path\B" `
            -Dockerfile "$Path\B\Dockerfile" `
            -Tag "B:$Tag";
    

That first clause automatically amends `Build-Maven` and `Build-Docker`
commands with `-ErrorAction Stop`. So if any of those build commands fail, the
entire script halts there. And, if I pass in `-Verbose` to this script, that's
forwarded to the build commands and I'll see the Maven and Docker build
output.

~~~
Arnavion
PS has another reason to use full names of parameters in long-lived scripts.
If you use short names like `-Ver` expecting it to match `-Verbose`, a future
version of the commandlet can add `-Version` and now `-Ver` is ambiguous.

On the other hand, I've seen some people eschew `%` and `?` in favor of
writing `ForEach-Object` and `Where-Object`, which in my opinion is too
extreme in the other direction.

BTW, in:

    
    
        if (-not $(Test-Path -Path "$Path" -PathType Container)) {
    

... you don't need the `$`, and if $Path is already a string you don't need
the `""` either.

~~~
ziml77
To me this isn't just another reason, it's THE reason to not use the short
form in scripts. Although unlikely, the worst case of this could be pretty
nasty. It's possible for someone to remove a flag that was deemed useless and
add another flag that matches the same prefix and has a very different effect.

I don't know how to feel about % and ?. The thought with not using them is
that they are just aliases and could be changed. But that reasoning sort of
breaks down since any command could be aliased to something dumb like `Set-
Alias Get-Content Remove-Item`.

~~~
Arnavion
Yes, exactly. If your script is running in an environment that has redefined %
and ?, it's either because they _want_ your script to use their definitions,
or it's busted beyond repair and you shouldn't worry about supporting it.

------
jwmhjwmh
If you're trying to stick to the POSIX standard, you have to use short options
for standard commands like grep, since the long options are GNU extensions.

~~~
Analemma_
Unless you count things like Makefiles, I don't think I've ever written or
encountered a "script" that was intended for use on multiple *nix flavors.
This strikes me as a YAGNI situation: do it if it comes up, but not before.

~~~
ori_b
This attitude is definitely something that makes my life harder. My OS is
usually OpenBSD, and I constantly need to fix other people's scripts.

It's not difficult to do, but it is annoying. I don't blame people for
ignorance, but it'd be nice if people thought about portability.

Non-portable scripts can also bite you on Linux, where Debian, for example,
will swap out the shell from bash to dash for performance. but others will
not. So, even within the Linux ecosystem, you can end up with scripts that
behave differently across distros.

~~~
rascul
> Non-portable scripts can also bite you on Linux, where Debian, for example,
> will swap out the shell from bash to dash for performance. but others will
> not. So, even within the Linux ecosystem, you can end up with scripts that
> behave differently across distros.

From the bash man page, "If bash is invoked with the name sh, it tries to
mimic the startup behavior of historical versions of sh as closely as
possible, while conforming to the POSIX standard as well." Also you should not
be including bashisms if your shebang is sh and not bash. So using bash as sh
shouldn't generally be a problem, if you stick to the published Shell Command
Language [0], unless you hit one of the cases where the spec is a bit
ambiguous and shells implement it differently [1].

[0]
[https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V...](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html)
[1] [https://stackoverflow.com/questions/16069339/different-
pipel...](https://stackoverflow.com/questions/16069339/different-pipeline-
behavior-between-sh-and-ksh/16322932#16322932)

~~~
ori_b
> _From the bash man page, "If bash is invoked with the name sh, it tries to
> mimic the startup behavior of historical versions of sh as closely as
> possible, while conforming to the POSIX standard as well."_

From what I remember, it doesn't do a particularly good job.

------
kazinator
Don't use long flags when scripting, if they have short equivalents, and POSIX
only specifies the short equivalents.

Even if the stuff will only ever run on one system, the POSIX flags (1) come
from a smaller set of options, since POSIX is fairly conservative in its
content this area and (2) are generally well known (often three decades old or
older).

Don't make me read a man page to confirm that "grep --fixed-strings" really is
the same thing as the POSIX-standard "grep -F", and not something subtly
different.

~~~
maxioatic
Why not just "man grep | grep -- --fixed-strings" real quick?

~~~
kazinator
These real quick moments add up to wasted time real quick.

~~~
khalilravanna
Wasted time for someone who has all of these memorized. How about all the
future wasted time of people having to look up the flags cause they’re not
explicitly spelled out? I’m in favor of explicitness because it saves time for
future people and gatekeeps less.

~~~
kazinator
-F is explicitly spelled out. Proof: I can see it. Explicit is the opposite of implicit, and implicit doesn't mean "abbreviated to a single character that is plainly visible".

You don't necessarily know what a long option means without looking it up,
just because it is long. Firstly, if your native language isn't English, you
may have to look it up in a dictionary. The ordinary meanings you find there
may not reveal what that word means in the context of the program. So, at that
point you're off to the documentation anyway.

I know what "hard" and "soft" are. Therefore, is it obvious what "git reset
--soft" means? Hardly.

Once you know what it means, "\--soft" jogs your memory by association a lot
better than some "-X". So that would seem like it is less cognitive work. But
when we have long options, it encourages the option vocabulary of a program to
keep growing, which adds to the cognitive load.

------
bondolo
I have been following this advice for about five years with my scripts and the
best result has been compliments from co-workers and others who have picked up
the scripts and felt like they had a much better understanding of what the
scripts were doing.

Combined with use of shellcheck and shfmt working in shell scripting has come
a long way in the last couple years. I still feel like BASH is a dead end--the
quoting and whitespace issues combined with the bifurcation caused by Apple
not shipping Bash 4.0+ make moving to Python probably the better course in
2020.

~~~
ramses0
It's so unfortunate about Bash + Apple.

Bash probably deserves to die out, but Apple has really pulled a bait-and-
switch on OSX's *nix/bsd underpinnings over the years.

~~~
saagarjha
Die out and be replaced by what?

~~~
stjohnswarts
Powershell? It's available for linux now

~~~
nobody9999
> Powershell? It's available for linux now

I'd rather have my tonsils extracted through my ears.

I went the other way[0] decades ago and never looked back.

[0] [https://www.cygwin.com](https://www.cygwin.com)

~~~
stjohnswarts
I was mostly joking. The verbosity of powershell just makes me throw up in my
mouth a little time every time I have to deal with it.

~~~
nobody9999
Oh noes! I've been Poe'd![0]

But I really would recommend Cygwin[1] for anyone who needs to use Windows.

Having a real shell makes a big difference.

[0]
[https://en.wikipedia.org/wiki/Poe's_law](https://en.wikipedia.org/wiki/Poe's_law)

[1] [https://cygwin.com](https://cygwin.com)

------
m463
In scripts, I usually do stuff like this

    
    
      command \
        --longopt1 \
        --longopt2 arg \
        argument
    

or

    
    
      command | \
        command 2 | \
        command 3 \
          --opt1 \
          --opt2 \
          arg

~~~
inshadows
It's nice but you cannot comment individual lines. For that you can go one
step further and use arrays:

    
    
      cmd=(
        command
        -x -y
        --bar OPTARG
        ARG ARG
      )
      $cmd
    

This works in Zsh. In Bash that would be ${cmd[@]} I think. I use this with
long qemu command lines where I often modify and comment out arguments during
testing.

~~~
tdons
You can comment using a hack; check this out:

    
    
      command `# a comment` \
        --longopt1 `# another comment` \
        --longopt2 arg \
        argument

~~~
inshadows
I didn't mean commenting arguments for documentation but commenting them out
while you're writing and testing the script. I.e. this won't work:

    
    
      command `# a comment` \
        # --longopt1 \
        --longopt2 arg \
        argument
    

AFAIK there's no hack for this.

EDIT: Oh, `# a comment`. Didn't notice that. I'm going to explore it, thanks.

------
leipert
I try to write long form now, but every time I encounter short form and don’t
know what it means I just use explainshell:
[https://explainshell.com/](https://explainshell.com/)

Long form also allows me to find things faster in man pages.

------
arendtio
I wonder a bit, that those POSIX comments are so far in the bottom of this
discussion. I mean, you are loosing portability by using long flags (because
it is not standard compliant) and yet it seems to be just a footnote in this
discussion...

~~~
ainar-g
Not a lot of people these days care about portability, unfortunately. You'll
be lucky if your project's build scripts run the same way on both Debian and
vanilla macOS. And when they don't, the answer people will give you is
probably something like “Just install GNU tools!”. *BSD? Forget about it.

------
entire-name
As the saying goes, code is typically written once and read thousands of
times. It takes you less than a second to write the full flag name (add a few
seconds if you need to look it up), but it will likely save at least a few
hundred readings the time to look up the flag if they do not already know it.
In addition, full flag names contributes "self-documentation" in many cases,
and also potentially makes searching easier in certain cases.

~~~
jfrunyon
You're assuming the long version is more well known than the short version.
That's a wildly and provably false assumption.

Do you know, without referring to the manpage, exactly what `cp --no-
dereference --preserve=links --recursive --preserve=all` does? I don't. I can
take some _guesses_ based on the names of the options, but those guesses could
very easily miss an important corner case.

Do you know what `cp -a` does? I do.

~~~
Alexendoo
The long form of `-a` would be `--archive`, for the same reason that you write
`-a` and not `-dR --preserve=all`

~~~
jfrunyon
I'm afraid that doesn't fit with the "be as explicit as you can regardless of
logic" espoused in the article.

------
moritonal
When writing scripts (CI especially) you'll rarely use, please always use
long-flags. It'll save so much headache for the next dev who isn't as up to
date with the latest short-hand for az-cli or whatever.

When live scripting, feel free to use short.

~~~
efrecon
I think you are right, for all "exotic" tools, long flags are much better and
future proof. For regular tools, short flags are so common that they are
probably ok.

~~~
watt
Your regular tool is not my regular tool. Hell, maybe my regular tool today is
not going to be all that regular in 3 years.

~~~
stjohnswarts
He's talking about thing like sed/awk/grep/find/xargs/tar not
new_hotness_util_2020

~~~
efrecon
I was. Thank you.

------
kvz
Agreed, bash3boilerplate.sh has long suggested that a few extra keystrokes in
a script pay off in saving your collaborators and future self trips to
manpages. Disclosure: b3bp author.

------
lucasmullens
Discussion from 2013:
[https://news.ycombinator.com/item?id=5164354](https://news.ycombinator.com/item?id=5164354)

------
nojvek
Great advice. Except a whole bunch of popular cli utils doesn’t have long
versions.

Like kubectl -f. In apply, it means file, in logs it means follow. Drive me
nuts how they overload short flags with different means but no long version.

Or gsutil/gcloud.

I get google folks, don’t really care much about always having a long readable
version of short flag.

------
mehrdadn
Great. Now try typing 'touch --no-create foo' on OS X and let me know how it
goes.

------
mikey_p
Went to try to update some of my scripts and stuff and found that most of OS
X/macOS uses BSD utils which don't have the long flags found in linux
versions. For example check out xargs:

[https://www.freebsd.org/cgi/man.cgi?xargs](https://www.freebsd.org/cgi/man.cgi?xargs)

[https://man7.org/linux/man-
pages/man1/xargs.1.html](https://man7.org/linux/man-pages/man1/xargs.1.html)

------
tyingq
I think adding the -- to the end, to stop option processing is advisable as
well. Where that's supported, of course.

Some may disagree, but unexpected flags passing through seems dangerous to me.

------
Someone
Are there editors that use autocomplete to suggest arguments?

That would make finding and typing the long arguments easier. As it is, it’s
easier to type the long arguments in a shell than in an editor.

------
jolux
I wanted to say that Microsoft explicitly says to use the full argument label
and not omit them when writing PowerShell scripts but I can't find the doc.

~~~
annoyingnoob
I was thinking that the author would really like PowerShell. PowerShell is the
most verbose thing I've ever used. I don't really like to type that much for
simple things.

I say just learn the flags or look them up, it shouldn't be hard or time
consuming. When reading scripts, many times you can infer what the flags mean
by understanding the inputs and desired outputs.

~~~
jolux
Sometimes, but well-written PowerShell scripts can be surprisingly readable,
particularly in comparison to bash.

------
ddevault
Absolutely not. Long flags are non-standard and non-portable. Don't write GNU
scripts - write shell scripts.

~~~
stjohnswarts
Depends on the situation as always. Obviously if it's going to be used cross
platform go for it, otherwise YAGNI

------
hashkb
Is there not a linter that can autofix this? Seems like it shouldn't be a
human responsibility.

------
atum47
never thought about that. I usually try to keep my code short to try to
impress other programmers, but that's really good advice

~~~
Jowsey
I'd say a painless, easy to understand program is more impressive than a short
one

------
jfrunyon
`rm -rf` is much more recognizable than `rm --recursive --force`.

`tar -cvf` is much more recognizable than `tar --create --verbose --file`.

Moreover, why are you writing a shell script if it's NOT quick and dirty?

~~~
Saser
Makefiles are one situation where one is essentially writing small shell
scripts, but which are not really supposed to be quick and dirty in my
opinion.

------
makz
No, thanks

------
alquemist

        --r<TAB>ecursive

------
inshadows
No, I'm not going to waste keystrokes and increase noise for lazy people.
Convince physicists to write force_gravitational instead of Fg, or functional
programmers to give up concepts from abstract algebra, and then I'll
reconsider. Until then, RTFM just like you do in other fields. curl is so
common you should already know what -s means and if not, well, RTFM.

