amarshall 1 day ago

Or you can just…not quote the tilde. Folks always seem to reflexively quote “strings” in Bash while not realizing that (almost) everything is a string and most strings are not quoted and it would be odd to do it (e.g. no one is doing `"ls" "-a" "foo"`).

  • cr125rider 1 day ago

    The trick is to quote explicitly and correctly. “ and ‘ are different.

    • dylan604 1 day ago

      why would you use smart quotes in a terminal like that?

      • airstrike 1 day ago

        why would anyone use smart quotes ever

        • mpyne 1 day ago

          They are typographically the right thing to have been using all along.

          Using ' and " to pretend to be ‘/’ or “/” is on par with the typewriter days where people would use the l key to stand in for 1 also. A justifiable approximation when technology limitations prevented using the real deal, but an approximation all the same.

          • Dylan16807 1 day ago

            Except l has a specific meaning that isn't 1, while the entire purpose of ' and " is quoting (and apostrophe).

            The equivalent of l for 1 is doing font-specific pseudo smart quotes with ` and '

            • opello 1 day ago

              That equivalency only holds if there is a visual distinction in the output between the l and the 1. There often wasn't when this practice was popular.

            • mpyne 1 day ago

              " and ' are themselves conjoined with other uses.

              " can be inches or arcseconds from cartography.

              ' can be feet (of measure) or arcminutes from cartography.

              These actually all have slightly different symbols, and the symbols for the left/right quotation marks are actually farthest from this approximation, even if they are the most frequent usage.

              It’s always been wrong in some respect to use a single available symbol to emulate three or more different typographical tasks, but quotation marks may actually be the one where the emulation is the most wrong.

              • kragen 1 day ago

                Overstriking ' was commonly used (on typewriters and ASCII printers) for acute accents, and overstriking " was commonly used for a diaeresis. The glyphs used in typewriter fonts and traditional (pre-Unicode) Unix terminal fonts are compromises between these different uses.

              • dasyatidprime 1 day ago

                Aside: in Unicode we now have ″ and ′ for that. Also the triple variant: ‴

                • robocat 12 hours ago

                  ′ Prime = Feet, minutes, or first derivative

                  ″ Double prime = Inches, seconds, or second derivative

                  ‴ Triple prime = Third derivative or specialized notation

                  And a diversion: https://en.wikipedia.org/wiki/Ditto_mark

          • dmd 1 day ago

            In fact, if we had been using left and right quotes from the beginning in shells, most “quoting problems” go away, as they’re all inherently rooted in not being able to know what level of nesting a quote character is at.

            • kragen 1 day ago

              m4 did in fact use left and right quotes from the beginning, but it still had other quoting problems. Really terrible ones, to the point of making the language unusably bug-prone for anything beyond very basic tasks, which is why it's mostly forgotten.

          • airstrike 1 day ago

            Can't that be handled by the typesetting/rendering software and displayed appropriately regardless of which specific character was typed?

            • kragen 1 day ago

              No, because it's missing semantic information carried by the distinction. The ’ at the beginning of

                  ’Tis brillig, and the slithy toves
              

              is not a left quote ‘ but an apostrophe ’, like the apostrophe in “can’t”; but ‘tis certainly possible that someone might want to put single quotes around a phrase beginning with the word “tis”, such as the Latin ‘tis misereri’. By contrast to “can’t”, the ‘ in “Hawai’i” is the ’okina used to represent the glottal-stop sound in the Hawai’ian language, not an apostrophe.

        • thaumasiotes 1 day ago

          As others have commented, for the same reason that we use smart parentheses instead of the more utilitarian |.

      • tom_ 1 day ago

        They probably fell foul of some browser text box auto-correct.

        • dylan604 1 day ago

          ' "

          I don't use WYSIWYG editors anymore, so I have to remind myself to use those when writing raw HTML text. Although, most browsers correct raw ' and " symbols in text now, I still try to use them to have compliant HTML

  • dylan604 1 day ago

    Unless you're running a shell command from python. That was the first time I saw a command string broken down into "string" arguments for every thing like that.

    • thwarted 1 day ago

      In that case, you're not running a "shell command" from python, you're passing arguments to exec. A shell command would be a string interpreted by the shell, and you'd use that for shell syntax things like having the shell do variable interpolation or redirections as part of executing the command.

  • vips7L 1 day ago

    Or just stop using bash. It’s a terrible language to write and has tons of footguns.

    • CodesInChaos 1 day ago

      Unfortunately half of its badness isn't isn't the shell itself, but the convention of how parameters are passed to processes on Unix systems.

      • akoboldfrying 1 day ago

        Well, the only other way I can think of that it could be done is the Windows way, whereby you pass the unparsed command line, spaces and all, as a string to the new process. And while this is arguably the cleaner interface, in practice it has meant even worse quote handling, since how -- or even whether -- double quotes are parsed now depends on the probably undocumented process startup code chosen by the program's compiler vendor.

        Want to quote a command line that may already contain double quotes, in order to pass it as an argument to some other program? No, you don't. It isn't right to want that.

        • ChrisSD 1 day ago

          The other other way would be more structured. Arguments are not just an array, they also contain `--switch` and key/value pairs (e.g. `--key value` or `--key=value` depending on who you ask). One problem with the flat array approach is there's no way to distinguish a literal value starting with `-` from a switch, which means there needs to be some way to workaround that (and users have to remember the workaround; how often do people remember to use `rm -- "$FILENAME"`).

          In practice almost every application does still need to do its own parameter parsing; a flat array is not enough.

          • NetMageSCW 3 hours ago

            VMS had a command description language that was used to define new commands and including allowed arguments and types.

        • NetMageSCW 3 hours ago

          Sure it is. How do you echo a command without executing it if the command is passed in?

      • formerly_proven 1 day ago

        Array of arguments is vastly superior and more secure than every program/runtime inventing a slightly different way of splitting a command string into an array of arguments. No debate. A real problem is the related birth defect in ssh2.

      • sysguest 1 day ago

        yeah but... that convention is so much of a security/bug headache

        sometimes, its footguns seem worse than javascript...

        hope some typescript-like "typed shell" becomes mainstream someday

        • coldpie 1 day ago

          > hope some typescript-like "typed shell" becomes mainstream someday

          The trouble with trying to invent a new, more robust shell language is you basically just end up re-inventing any number of scripting languages (eg Perl, Python, awk, ...), so you might as well use one of those.

          • hnlmorg 1 day ago

            Most regular programming languages aren’t well suited for shells because they have a verbose syntax due to their readability goals. But with a shell, the vast majority of times you’re typing in stuff that you have no intention of reading back ever again.

            I’ve done a fair amount of research here and I actually think we do need a new programming language for the shell (and then I created one).

            I wrote a blog about this problem: https://murex.rocks/blog/split_personalities.html#conclusion

            • coldpie 1 day ago

              Excellent article, thanks for the link. I do agree with the premise. But every time I write some Bash code and think there should be a better way to do this, I ask myself why I don't just learn Perl. I dunno. Feels like inventing another solution would just hit the old "there are now 14 standards" problem where it would solve some problems but introduce others (see: PowerShell).

              • hnlmorg 1 day ago

                I don’t disagree with you per se. But if the new shell solves enough annoying problems then I think it has merit in existing.

                • NetMageSCW 3 hours ago

                  But then you run into the problem that you’ve learned a new language and built new scripts that only you can use.

                  I use Take Command (well, mostly TCC) in my Windows workstations but no one else does, so TCC specific batch files and aliases only work for me.

          • Greed 14 hours ago

            > so you might as well use one of those.

            Did someone say... Xonsh?! It's tough to give up the niceties of zsh and it's various plug-in ecosystems, but I would love to be able to use Xonsh [1] as a daily driver for exactly the reasons stated. It is already the language I lean on over bash for most utility scripts with its kitchen sink built-ins.

            [1] https://xon.sh/

            • coldpie 8 hours ago

              Oh this looks quite nice!

        • colejohnson66 1 day ago

          PowerShell

          • NetMageSCW 3 hours ago

            Powershell syntax is worse than bash argument quoting and it’s half-hearted participation in .Net instead of being designed as built-in makes it annoying to do anything advanced, plus its security defaults are almost never what is wanted to actually get stuff done.

      • hnlmorg 1 day ago

        That’s not correct because POSIX passes what is ostensibly an array of strings.

        Windows, on the other hand, only passes one string. So it’s up to the application to choose how to handle whitespace, quotation marks, and other nuances with parsing parameters.

        Variable expansion in Bash is lazy. But there’s no reason why variables cannot be tokenised so that strings with spaces aren’t treated as multiple parameters. And in fact that’s exactly how some other shells work, such as the one I maintain.

        • CodesInChaos 1 day ago

          While unix handles splitting into an array of strings, it still leaves parsing those strings up to the application. One of the bigger problems is the ambiguity caused by filenames starting with `-`. Some applications support the `--` marker to end such parsing, but it's inconsistently implemented and the caller has to actively remember to use it.

          A typed array (e.g. by having a required single byte marker at the start of each string), or even nested structures similar to s-expressions would have avoided this.

          • hnlmorg 23 hours ago

            Funny enough, I’ve actually spent a long time trying to figure out how to solve this problem in murex but it always comes back to the same problem: anything smart I implement into the shell is immediately lost the moment I call fork().

            I even considered writing a wrapper around some commands to send -- regardless of whether the user includes it or not. But that’s error prone too because

            1. it’s a GNUism so you can’t guarantee compatibility with any tools that don’t call GNUs flag parsing library (which is particularly problematic outside of Linux)

            2. If there a file called “-f” (for example), how do you know if the user intended that to be called as a file reference or a command flag? You then need to bake in a bunch of additional syntax sugar to make that explicit. Which results in a pretty awful user experience

            3. The maintenance overhead for this would be astronomical

            If UNIX were designed from day one to have that parameter type passed then this would be a very simple problem to solve. But the PDP machine UNIX was originally designed to run on wouldn’t have been powerful enough for that anyway. So once again we are limited by an architecture designed to run on mid-range systems of the 1970s.

    • malux85 1 day ago

      The widwit answer: "Dont do X" (and nothing more)

      The enlightened answer "You should use A, B or C for these reasons"

      Even though bash is installed on many systems, I try to encourage people to use better designed shells that have less of these footguns :

      Fish (https://fishshell.com/): No implicit word splitting : spaces in variables won't unexpectedly become separate arguments.

      Zsh (I use this: https://ohmyz.sh/): Arrays start at 1 by default, but crucially, unquoted variables don't implicitly split into multiple arguments.

      Nushell (https://www.nushell.sh/): Passes structured tables and records between commands : avoids fragile parsing of text with awk/grep.

    • yjftsjthsd-h 1 day ago

      For the things shell is good at (running other commands, pipes, and shuffling files), I have yet to find anything even close to as good.

      • hnlmorg 1 day ago

        I agree that shells are uniquely good for that but there are plenty of better shells than Bash.

        Such as Fish, nushell, Elvish, or the project I help maintain, “murex”

        • bornfreddy 1 day ago

          None that I can rely on being available wherever I see a terminal. It's bash or sh as far as I'm concerned.

          • xigoi 1 day ago

            Fortunately, 99.9% of the time I’m on my computer, where I can install whatever I want.

            • ifwinterco 1 day ago

              Then you ssh into an instance and have to remember how bash/sh work

              • gavmor 1 day ago

                At what point does an alternate shell get baked into the image?

              • hnlmorg 23 hours ago

                I can’t think of many occasions where I’ve needed SSH access to a box (ie they’re not just a fleet of ephemeral systems) but I’ve also not had the permission to install an alternative shell on it. I’m not saying it never happens. But it’s not as common a problem as it was 20 years ago.

                Unfortunately though, it’s not generally that hard to switch between different shells. I hear the argument you made a lot from other people too and yet we all acknowledge you should be able to code in Bash and a “proper” programming language (Python, Go, JavaScript, C, Rust, whatever). In fact I’ve learned probably 2 dozen languages in my career and it’s rare when I need to pause and remind myself how to do x in y.

                Or to put it another way: you don’t say “I’m not going to use $IDE on my dev machine because I’m stuck with vi on the remote server”.

                But if people don’t feel comfortable working in two different shells and that is the dealbreaker for using murex, or any other alternative shell, then I’m not going to hold that against them. Everyone has their own preference when it comes to workflows and productivity tools.

              • xigoi 12 hours ago

                Whenever I’m SSHing, I am not doing complex stuff that would require intricate knowledge of the shell, so it suffices to remember the basics.

      • vips7L 1 day ago

        A different shell??? Nushell, fish, powershell, etc.

        • lelandbatey 1 day ago

          None of those have the #1 best thing bash is good at: bash is already installed, nushell and fish are not.

          Powershell is, as I understand it, not available on Linux but is omnipresent on Windows, so hopefully the windows folks can use it like they would bash.

          • yjftsjthsd-h 1 day ago

            No, powershell runs on Linux, it just sucks because it wants objects and everything on *nix speaks streams of text.

            • NetMageSCW 4 hours ago

              Arguably that is an improvement over streams of text that have to be re-parsed in every utility and sometimes incorrectly since quoting is problematic. OTOH, Powershell lacks a nice way of dealing with the objects pieces and streams the way e.g. LINQ does, or perhaps LISP can.

          • barrkel 1 day ago

            PowerShell works on Windows because of WMI, to a first approximation.

            Because Windows is tied together through APIs and databases and not text files (unlike Linux), everything is text is not as useful as something that can talk objects. And a lot of Windows is object oriented, from the window message dispatching system through COM and down to NT's object manager.

          • NekkoDroid 7 hours ago

            > Powershell is, as I understand it, not available on Linux but is omnipresent on Windows

            Do note that Windows Powershell and Powershell are NOT the same. The following does work in Powershell, but not Windows Powershell.

                echo hello && echo hello
            

            Specifically Windows Powershell does not support `&&` or `||` (the main difference I immediately notice) and probably a lot of other things are different.

        • yjftsjthsd-h 1 day ago

          Oh, you mean bash specifically. Then yeah, that's reasonable enough. Although I agree with the other commenter that sh/bash have the advantage of ubiquity.

      • shevy-java 1 day ago

        I did.

        Ruby replaced all my shell needs. Almost 25 years ago. I even have a shell written in ruby (it handles both bash-like behaviour as well as ruby code as-is); admittedly it is not quite perfect for everything, but I improve on it steadily. And it works on Windows too, which was one reason I wrote it in the first place (need to have it work via cmd.exe as-is).

        Never looked back to shell. It is too awful to use.

    • m463 16 hours ago

      so... I have a sort of rule for scripts I write.

      shell - simple scripts of a low size/complexity

      readable.

      exceed that and I switch to python. Right off the bat, argparse and os.path make so many things reliable/secure/better.

      Thing is, some of my coworkers don't know linux, so AI writes their scripts.

      When I'm consulted on a review, it is pages and pages of shell scripts.

      It is not easily readable stuff. There are lots of obscure/advanced bash features, like "if [[", and arrays and set (wierd option) and more...

      I have to slog through it like I'm reviewing 100m of dense line noise. I despair. Even reading the CRC handbook or war and peace would be more pleasurable.

      And in the end it is like reading a hostile privacy policy that I am obliged to accept.

  • paulddraper 1 day ago

    People quote both too often and too little.

    GENERAL RULE

    1. Double-quote dollar sign expressions, and nothing else.

      foo
    
      "$bar"/foo
    
      baz:"$(cat example.txt)"
    
      exec cmd "$@"
    

    2. Single-quote words with a literal special character, and nothing else.

      'Die Hard'
    
      'ke$ha'
    

    ---

    I should point out that the author's example is NOT fixed by different quoting though.

      # original
      export PATH="$PATH:~/.local/bin/"
    
      # without unnecessary quotes
      export PATH="$PATH":~/.local/bin/
    

    Because tilde expansion only happens at the beginning of the word.

    • JdeBP 1 day ago

      The Z shell's, C shell's, and others's syntaxes for setting the PATH environment variable via a shell array variable alias also does the tilde expansion.

          path=( $path ~/bin )
      

      It's worth noting, also, that the path and manpath settings in login.conf(5) expand leading tildes in individual search path items.

      * https://man.freebsd.org/cgi/man.cgi?query=login.conf&sektion...

      So putting the addition of things like ~/bin to PATH in /etc/login_conf and ~/.login_conf instead of shell scripts is another way to address it.

          :path=~/bin /usr/local/bin /usr/pkg/bin /usr/bin /bin:
      

      It's particularly handy when there are multiple login shells in use.

      * http://jdebp.uk./FGA/BSDs-for-Linux-users/login-conf.html

    • gray_-_wolf 1 day ago

      > I should point out that the author's example is NOT fixed by different quoting though.

      Sure, but I would say you almost always want to add to the start of the PATH, not to the end.

      • GrinningFool 1 day ago

        I used to put it first for convenience, but got increasingly paranoid about something same-named getting slipped into my ~/.local/bin; now it lives at the end.

    • Izkata 19 hours ago

      > I should point out that the author's example is NOT fixed by different quoting though.

      > Because tilde expansion only happens at the beginning of the word.

      Nope, works fine:

        $ PATH=/bin/
        $ export PATH="$PATH":~/.local/bin
        $ echo $PATH
        /bin/:/home/izkata/.local/bin
      

      I guess it's being smart about the ":" since this doesn't work:

        $ PATH=/bin/
        $ export PATH="$PATH":foo/~/.local/bin
        $ echo $PATH
        /bin/:foo/~/.local/bin
      

      And even just putting the ":" inside the quotes and "~" outside doesn't work:

        $ PATH=/bin/
        $ export PATH="$PATH:"~/.local/bin
        $ echo $PATH
        /bin/:~/.local/bin
  • Cockbrand 1 day ago

    I feel like this is common knowledge and should not be worth mentioning. But then it apparently is not common knowledge, as the article proves.

    Have I run into this at some point?

    I certainly have.

    Have I learned to quote better and only where appropriate from it?

    I certainly have.

    Bourne compatible shells take a while to learn and require some experience. This won't change, but alternatives exist, with their own caveats.

    • MathMonkeyMan 1 day ago

      Almost nobody that I've ever worked with knows how the shell works. I send them [the docs][1] but what kind of world would this be if people read the docs? Also, not exactly a page turner.

      [1]: https://pubs.opengroup.org/onlinepubs/9799919799/utilities/V...

      • jolux 1 day ago

        also poorly organized and formatted.

      • Cockbrand 1 day ago

        I learned this stuff from friends, O'Reilly books and endless nights of failure and trial and error. I'm certainly not a super wizard, but I feel like I'm not missing any shell skills to be able to do what I need to do.

        [Insert mild rant on kids these days having no attention span any more]

        • alsetmusic 17 hours ago

          > [Insert mild rant on kids these days having no attention span any more]

          We used to try a command and then alter it slightly to see what it would do.

          • NetMageSCW 4 hours ago

            I recommend not doing that with rm.

      • koolba 20 hours ago

        > Also, not exactly a page turner.

        Really? I can’t be the only one that will spend hours reading this kind of stuff. You guys don’t read man pages for fun?

        • massysett 19 hours ago

          I spent countless hours poring over the multiple man pages for zsh, along with books such as "From Bash to Zsh" and the book Peter Stephenson wrote. I wrote up decent completions for some software I wrote for personal use. My software is completely non-interactive and relied on complex trees of commands. My thinking was that completions would make it usable. But my completions were buggy and I didn't fix them for years (I'd use the completions, hit TAB, get some error messages, and say "oops, let me do this without the completions." For years.)

          Years ago I thought this was fun. Now I've given up on learning zsh _arguments and the like. Instead I have made my software more interactive (an LLM helps here) and wrote it in Common Lisp (even without an LLM) so that I get decent completion behavior in SLIME for free.

          I actually do like reading these things. But big shells like Bash and Zsh are brutal, especially with their random grab-bags of features that come from multiple shells of years long gone.

          • NetMageSCW 4 hours ago

            If your completions aren’t based on your parsing you are doing it wrong. See DEC VMS.

    • vrighter 13 hours ago

      it is not common knowledge that in bash everything is a string. And if it is, the consequences of such a thing is most definitely not common knowledge.

  • chasil 1 day ago

    I think the appropriate method by POSIX rules would be:

      export PATH="$PATH:"~/.local/bin
    

    I may be wrong. If I'm not, that works in any POSIX-compliant shell.

    Edit: this appears to work properly with mksh on my phone:

      :/ $ export PATH="$PATH:"~/.local/bin
      :/ $ print $PATH
      /product/bin:/apex/com.android.runtime/bin:/apex/com.android.art/bin:/system_ext/bin:/system/bin:/system/xbin:/odm/bin:/vendor/bin:/vendor/xbin:~/.local/bin
    • yencabulator 1 day ago

      That's a literal ~ in your path, the exact problem the blog post talks about.

      Your use doesn't count as a "word", per man bash. Tilde is only expanded to home at the start of a typically whitespace-separated word, and your tilde is in the middle of one.

      > If a word begins with an unquoted tilde character (‘~’), all of the characters up to the first unquoted slash (…) are considered a tilde-prefix. (…)

      > word A sequence of characters considered as a single unit by the shell. Also known as a token.

      • chasil 1 day ago

        My initial syntax does work in dash (and bash), but all these shells appear to rely on expanding ~ inside a word, that your source asserts is not POSIX (which I do not contest).

        Perhaps a succinct and compliant expression could be:

          export PATH="$PATH:$(printf %s ~/.local/bin)"
        

        That comes at the cost of forking a subsell.

        Edit: 2.6.1 Tilde Expansion in the POSIX standard says that ~ may be expanded "following any unquoted <colon>".

        https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V...

        That being so, the most succinct and compliant version is my first variant, with the colon moved outside the quotes:

          export PATH="$PATH":~/.local/bin
        

        Thank you for prompting me to look this up.

  • Grimeton 1 day ago

    You need to read the manual.

  • isityettime 1 day ago

    This feature, or at least the syntactic unit in question has a name here "bare words". You also have these in some contexts in Perl and Ruby, YAML, and probably some other languages, idk.

    I've also seen this even with people who seem like generally competent shell users. Idrgi

  • kragen 1 day ago

    Not quoting the tilde doesn't help:

        : tmp; echo $PATH:~
        /usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:~
    

    Edit: mjmas points out that I am wrong, because different Calvinball rules apply to variable assignments:

        : tmp; x=$PATH:~
        : tmp; echo "$x"
        /usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/home/user
    

    Also in general your advice is very bad advice. In command-line arguments, which is the vast majority of the code of any shell script, you should always quote strings that contain variable expansion unless you want them to be implicitly split on spaces after variable expansion, because the thing you're storing in the variable is not an atomic string but rather a space-separated list.

    This is almost never what you actually want, and in the rare cases that you do want to store a list, the shell's implicit space splitting usually breaks on filenames containing spaces. Bash has actual array variables which make it possible, but horrible, to handle this in a first-class way:

        : tmp; x=("foo bar" baz)
        : tmp; echo "${x[@]}"
        foo bar baz
        : tmp; touch "${x[@]}"
        : tmp; ls -l "${x[@]}"
        -rw-r--r-- 1 user user 0 Oct 11 17:52  baz
        -rw-r--r-- 1 user user 0 Oct 11 17:52 'foo bar'
    

    This ksh feature is absent in the Bourne shell and in dash, but MirBSD ksh, Bash, and zsh all have it.

    In general, whenever you see a $variable $expansion in a shell script outside of double quotes, you should suspect that the shell script will probably fail if your filenames or directory names contain spaces. In very many cases, this results in path injection security vulnerabilities. There are contexts where unquoted $variable $expansion is safe, but they are relatively rare.

    However, relevant detail here! One of those safe contexts is actually variable assignment, where as mjmas pointed out in their helpful comment below, unquoted variable expansion is actually perfectly safe:

        : tmp; a='x  y'
        : tmp; b=α:$a:ω
        : tmp; echo "$b"
        α:x  y:ω
    • mjmas 1 day ago

      Bash does it only inside variable assignments:

        a=123:~
        echo $a
        echo 123:~
      

      results in:

        123:/home/me
        123:~
      • kragen 1 day ago

        Oh, thank you for the correction. I was wrong about that. Calvinball.

    • theowaway213456 22 hours ago

      What's the `: tmp;` incantation for?

      • kragen 22 hours ago

        Sorry, that's my shell prompt. It's designed to be harmless if you accidentally copy and paste it — the opposite extreme from tcsh's default prompt of >.

    • amarshall 2 hours ago

      Certainly I would not say “don’t ever quote strings in Bash”, that’s plainly wrong. Actual advice I would give is to use Shellcheck for all shell scripting as it will generally correctly warn about missing quoting (e.g. of shell vars) and does warn about tilde not being expanded in quotes.

      It does not warn about `echo "$PATH":~`, though, but that is perhaps an uncommon “error” that is more about your contrived test case (not that Calvinball is good, ofc).

  • ndsipa_pomu 8 hours ago

    Quoting strings in BASH is more to do with variables that may possibly contain a space. If someone had a home directory of "/home/ndsipa pomu/", then not quoting it could potentially cause issues and bizarre bugs.

    Personally, I don't like using the tilde and would use "${HOME}" instead (I nearly always put variables in curly brackets as a matter of habit).

SoftTalker 1 day ago

I never use tilde in scripts, only as a convenience when typing commands interactively.

$HOME otherwise, which still has gotchas but they are the same as any other environment variable.

  • ndegruchy 1 day ago

    Yeah, in scripts I try to be as explicit as possible. `$HOME` for the home directory, `$XDG_DATA_HOME` for `/home/foo/.local/share/`, etc.

    The more specific you are, the less gotchas you're going to fall to.

nlehuen 1 day ago

I refuse to let knowledge about Calvinball-level grotesque rules about quoting and escaping in bash encumber my mind.

  • weinzierl 1 day ago

    Good decision and avoid the shell whenever you can. Too late for me. I did my fair share of shell programming (and learned if from some real masters) so that my mind is already encumbered. My confidence in being able to write a longer or more complex shell script with 0 errors is still 0, so it gives me nothing.

FeepingCreature 1 day ago

Kind of seems like you should have noticed this by your ~/.local/bin PATH not working?

  • ndsipa_pomu 8 hours ago

    There's not usually many, if any commands in you .local/bin, so likely not noticeable until you install something like "yt-dlp" (the only thing I found).

  • NetMageSCW 3 hours ago

    Feels like it would be even more sensible to just check that PATH is now what you expect after making changes?

alexpotato 1 day ago

Not necessarily about tildes but about some of the craziness that can happen with bash at large orgs:

At a past job, I was trying to figure out what part of my basrhc was setting a particular environment variable. I assumed that it must be some kind of default installed in my user profile and/or inheriting from /etc/<something>.

I realized pretty quickly that my bashrc was importing some other files. Again, the assumption was that this would be one file deep in the import.

It turned out there were 10+ layers of import starting from an "ur-bashrc" and then layer upon layer of more and more imports to finally get to a user level profile.

It was so convoluted that I was going nuts until I found this Stack Exchange post: https://unix.stackexchange.com/questions/813/how-to-determin...

It turns on "tracing" for bash imports so that you can then narrow down on where the env variable is getting set.

thefilmore 1 day ago

You can just do:

  PATH=~/.local/bin:$PATH

PATH is already exported. Quotes are also not necessary for assignments.

  • stkdump 1 day ago

    Looks dangerous to put a user writable directory ahead of system directories in PATH

    • bityard 1 day ago

      Nah, it's totally normal because you often want to deliberately override or wrap system commands for various good and convenient reasons.

      If your concern is that some hacker could put an illicit binary in ~/.local/bin, then your security problems are much deeper than your PATH order.

  • russellbeattie 1 day ago

    While we're all getting pedantic and esoteric, in zsh you can do this:

    path+=(~/.local/bin)

    You don't even have to use the "export" keyword. Not that I would do this, but since we're all pointing out random shell stuff...

    (In zsh the lowercase "path" variable is an array that's automatically tied to the $PATH string, so you can just add to it using parens to signify new array elements, separated by a space.)

dspillett 1 day ago

I was going to say “it always seems to work for me” then I saw “… actually works in Bash and Zsh, because …”.

Another Bash-ism I need to be careful not to use when trying to be portable.

It is worth noting that on a lot of systems /bin/sh isn't bash (or zsh) so if you want to rely on Bashisms (or just can't be bothered looking for them) be specific and use “#!/bin/bash” for you hashbang. On Debian and similar it is usually dash for instance.

  • opello 1 day ago

    For various embedded environments that use busybox it's busybox's brand of ash. I generally enjoy the opportunity to learn when bumping up against these kind of edge cases.

    • dspillett 23 hours ago

      Aye. Even though I'm more than happy to rely on bashisms in my normal work/play, I like to know where the edges are. Even if I don't know off the top of my head what the POSIX way is for something I usually use the bash sugar for, when I need it I know I need it and how to find it.

m463 16 hours ago

I've always wondered if bash/zsh/etc should have a "strict path" setting.

Meaning everything in the path should resolve to sane stuff, like no relative paths, don't allow PATH=.:$PATH and no literal '~' characters (or even space/etc?) maybe check all are directories and exist.

the__alchemist 1 day ago

I wish Linux distros would ship a "Terminal"/"CLI" program etc that is decoupled from the scripting language. Have a universal Path env var that isn't tied to a specific shell. Lets you execute cd commands, launch python/git/cargo/arbitrary applications etc, and have a good bookmark + autocomplete system. It feels like the conflation of scripting language + CLI application is the root of these complications and subtleties.

If you are using shell scripting (And prefer Bash etc over Python), you would keep using Bash/Fish/Zsh etc. If you are using the CLI to launch applications that don't have a GUI, navigate directories and perform file system operations, then you would use the plain terminal.

  • akoboldfrying 1 day ago

    IIUC, you're describing an extremely restricted (some would say underpowered) shell.

    It sounds like you could make it yourself in ~10 lines of Python or bash. I don't see it catching on, though.

  • Lerc 1 day ago

    I always thought that there should be a proc style file system for simlinks to user specific data.

    I'm not sure why this can't be done. Security people will have a million reasons, I guess it's possible one of them might be valid.

  • dicytea 1 day ago

    $PATH is not tied to any specific shell. And what you're describing is a shell, so I'm not sure how it's any different from existing solutions.

    • the__alchemist 1 day ago

      It is - this is why adding something to the Path is tricky on Linux. Or I should say, there is a mismatch between the common instruction of how to do it (export) vs what you have to do (e.g. edit bash config)

      • hnlmorg 1 day ago

        That’s because there isn’t a universal PATH variable.

        Env vars are not global. They just have that illusion because a fork() by default will pass your running env vars to the child. Thus trickling that value down.

        • the__alchemist 1 day ago

          Exactly. It's annoying if I'm just trying to launch an application I installed by typing its name, which is IMO what PATH is for.

          • hnlmorg 1 day ago

            How are you installing it? Normally package managers will symlink it into a system defined $PATH directory so you shouldn’t need to faff with $PATH yourself.

            Not all applications executable match the application name (eg Visual Studio Code is just “code”). So could that have been the issue?

            • the__alchemist 1 day ago

              Depends on the software. Generally, whatever the official installation instructions are.

              > So could that have been the issue?

              No. Adding things to the path is a reasonably common operation on all OSes and CLI-based software. Sometimes the installer will handle it for you; sometimes it won't. (This may be out of date, but I believe the Rust install script is an example of one that sets it up; Go is one that doesn't, and its official instructions CAO 6 months ago were to use Export, which doesn't work once you reboot)

              • hnlmorg 23 hours ago

                go install isn’t really an installer. Frankly I don’t really see the point of even having that option.

                More broadly speaking, you shouldn’t really be relying on your compiler to do the install. That was never the intended behaviour of a compiler. Which is why history you’d find the install options in the build scripts (eg makefile).

                • the__alchemist 21 hours ago

                  I was referring to installing the Go compiler - as an arbitrary example of an application you want to be able to launch from CLI, but that doesn't set it up for you. (And instructs (Or did) you to use export.

                  • hnlmorg 14 hours ago

                    That’s because go is normally installed via package managers. If you want to install it manually then you need to define the environment manually so that it doesn’t conflict with any system-installed version that might depend on a particular behaviour of the one bundled with the package manager.

                    Distro maintainers test dependencies across all these different pieces of software, but they cannot test against people randomly installing their own versions of software. So it would be negligent for the Go team to advocate overwriting the distro-tested version of Go

                    • the__alchemist 10 hours ago

                      https://go.dev/doc/install

                      Installing arbitrary software, including through the creator's official instructions, is a core capability of a GPOS.

                      > people randomly installing their own versions of software.

                      There are 2 things to unpack: Why you are referring to an official release as "randomly installing my own version", and that installing arbitrary software isn't a core component of a GPOS. Further, what does a call-to-only-use-package-managers (Or did I misunderstand you? That's how I'm reading it) has to do with the PATH on linux being terminal-specific?

                      • hnlmorg 7 hours ago

                        > Installing arbitrary software, including through the creator's official instructions, is a core capability of a GPOS.

                        No, it’s actually not. And when you look at the most popular platforms out there, they do have a primary channel for installing software.

                        > Why you are referring to an official release as "randomly installing my own version",

                        It’s not an official release from your OSs package repository. Which is what I actually said.

                        > and that installing arbitrary software isn't a core component of a GPOS.

                        I didn’t say that either. I said you’re installing it outside of the package manager so it shouldn’t mess with the platform defaults because it could unknowingly screw up another part of the system that the maintainers are depending on.

                        If you were to install your own C++ runtime onto Windows and overwrite the Windows default, causing Windows to develop a plethora of bugs. Would you then complain to Microsoft?

                        This is the equivalent of what you’re doing on Linux and which is why the Go official documentation talks about doing it in a more local scope. Just Google any of the stack exchange and forum discussions around the dangers of updating the system version of Python and Perl. People have done real damage to their systems by overriding the distros shipped version.

                        • the__alchemist 1 hour ago

                          > ```Installing arbitrary software, including through the creator's official instructions, is a core capability of a GPOS.

                          No, it’s actually not. And when you look at the most popular platforms out there, they do have a primary channel for installing software.

                          ```

                          We have very different ideas of what we want out of an OS! I rarely install applications from package managers because the software I use is usually not on them, or is an out of date version. I don't post my own software on them as I'm not going to register and post updates for each OS. I do not think software should be fragmented across incompatible OS app stores.

          • arccy 1 day ago

            this is why you don't curl | bash

  • delta_p_delta_x 1 day ago

    > Have a universal Path env var that isn't tied to a specific shell

    This sounds like Windows. ;)

    Environment variables are stored in the registry; for the user, it's

      HKCU\Environment
    

    and for system-wide env vars, it's

      HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
OroPla 23 hours ago

Never ran into this, as I never thought to put quotes around a tilde in my 20+ years with bash.

I guess it is interesting that ~ behaves differently from a variable, as usually you'll have to use single quotes to get the string literal, but then I wouldn't want the tilde to expand inside quotes, either.

ligarota 1 day ago

Juste create a file ~ which is a symlink to home :)

Big brain time here

  • mnw21cam 1 day ago

    In every single directory you're ever going to have as cwd when running that software?

ultraboom 1 day ago

The bash man page is comprehensive, precise, and well-written. A recommended read for any bash user.

em-bee 1 day ago

in the days of yore, when i still was a green linux/unix newbie i attempted to use the mirror tool (written in perl i believe) to create a mirror of something on my account. i configured the tool to write the files into a directory in my home. in the config i wrote ~/somewhere ...

after running the tool, i discovered a literal directory named "~".

that's wrong i thought, fixed the config and proceded to remove the offending directory:

rm -r ~

then i waited ...

and waited ...

and i started wondering why removing an almost empty directory is so slow ...

...

...

oh sh!!!!!!

...

  • em-bee 1 day ago

    i have no recollection of what i lost that day, but i learned a few lessons:

    prefix ambiguous paths with ./

    rm -r ./~ would have worked.

    don't use rm -r when you can avoid it.

    rmdir -p ~/something would have failed without causing damage.

    rmdir -p ./~/something would have been the best option.

    if there are files but no subdirs inside then

        cd dir
        rm ./* (if there is a more specific wildcard that catches all files then use that)
        cd ..
        rmdir dir
    

    is my preference now.

panzi 1 day ago

I expected it to also talk about ~username. See e.g.: echo ~root ~pulse ~sddm

very_good_man 1 day ago

Reminds me of early days of Cursor when it decided it would be a good idea to create a directory named "~" in my repo root!

That was a scary mistake to unwind!

  • Ekaros 1 day ago

    Just maybe, just maybe reserving somethings is not a bad idea... Whatever the fanatics say... There is enough features in shell that maybe banning somethings would be correct design.

    • NetMageSCW 3 hours ago

      The problem is what do you reserve? The shell is not the filesystem and not everyone uses the same shell. No reason for ~ to be treated specially by Powershell.

arkt8 1 day ago

It is a clear example of RTFM! An expansion is no a variable. An expansion not expands under quotes. A variable expansion is not simply an expansion as it is between brackets.

Again RTFM instead of wait for a miracle of your agent.

  • 3eb7988a1663 1 day ago

    Ehh, there are many, many (documented) gotchas about commonly used software that routinely bite people. Yes, it would be great if everyone were an expert in how every aspect of their toolkit works, but that is not practical.

quotemstr 1 day ago

I was expecting this article to be about skew between HOME environment variable and getpwent(3) (usually from /etc/passed) views of the home directory.

They can be different things. Suppose your user is foobar, ordinarily homed at /home/foobar. You can write HOME=/tmp/my-test-home. Then, ~/qux will become/tmp/my-test-home instead of the usual /home/foobar/qux. That's because bash and zsh use HOME to resolve ~.

Almost. If you write ~foobar/qux, you get /home/foobar/qux again because the ~-with-username syntax looks up the getpwent home directory not the environment one.

And of course language runtimes are all schizo about whether the "user home directory" API uses HOME or the getpwent database or whatever to determine the home directory.

It's a mess, TBH. It used to be useful to temporarily bind HOME to something else to do things like create isolated test environments. Now, because of the aforementioned schizo sprinkler of randomness in the environment, you're going to have a bad time if you don't keep HOME synced to getpwent home directory.

Grimeton 1 day ago

People only reading the manual after they shot themselves in the foot.

Hilarious!

piekvorst 13 hours ago

~ is a design mistake.

  • ButlerianJihad 13 hours ago

    IIRC, ~ is a construct of csh. csh is the classic Unix example of "mistakes were made!"

    csh was also the most usable interactive shell in the early BSD days, since it had full support of job control, which was indispensable for power users before the advent of screen, and without the flexibility of windowing such as X11.

    csh was really atrocious for scripting, though. Any competent programmer would eschew csh scripting in favor of Bourne or Korn. (In fact, we also avoided Bash like the plague, due to all the nonstandard extensions and initial non-portability.) I had friends whose employers sort of mandated csh scripts, and I pitied/mocked them for a long time. It was always a bad decision.

    However, I'm gonna say that "~" was also a triumph of csh syntax, because, well, it was adopted into everything else! It's an indispensable shortcut now, and like job control, it has survived the acid test that ultimately killed csh and most of its stupidities. Just look at how it was seamlessly incorporated into web URLs.

    An important distinction for "~" is how it can expand others' $HOME with an /etc/passwd lookup! There is no environment variable that's capable of that. It's such a command-line timesaver for people who collab.

    ~ is ubiquitous now, and that's not a mistake. Indeed, script programmers should adhere to ${HOME}, but when it comes to command-line quickies, there's noplace like tilde!

duncangh 1 day ago

I should have read the docs before getting ~/ tattooed on my wrist.

  • lysium 1 day ago

    If you haven’t quoted it, I think you’re fine.

    • duncangh 1 day ago

      sometimes I feel like my environment isn’t properly configured but that’s just as likely a user error

mrsssnake 1 day ago

Is there some tool, language or way to early script together inputs and outputs or external programs, with some safety or "proper" (for lack of better word) programming languages but with convenience of Bash (no wrapping like "exec(programname)"?

  • lexicality 1 day ago

    Perl has been the sysadmin's friend (and enemy) for decades. You can run stuff with backticks and manipulate the results in relative safety.

NotMichaelBay 1 day ago

Alternative title: There's no place like $HOME

gjvc 1 day ago

bad example in the article:

  export PATH="$PATH:$HOME/.local/bin/"

better:

  export PATH="$HOME/.local/bin/:$PATH"
  • Backslasher 1 day ago

    Afaik it's habit to give system paths precedence so a malicious script can't shadow e.g. sudo and steal your password, escalating a local file write into root

    • paulddraper 1 day ago

      AFAIK it's habit to allow your scripts to override system ones, so you can customize behavior.

      I've always seen home dir, homebrew, etc prepending to PATH.

    • toast0 1 day ago

      Otoh, if you don't put your local path first, you can't override system binaries that you want to override.

      Also, if something can write into your path, it can probably write to your shell config and/or the environment variables.

      • stkdump 1 day ago

        Why would you want to override system binaries

        • toast0 1 day ago

          Often, system binaries are ancient

          • BenjiWiebe 1 day ago

            Are you a MacOS user by any chance?

    • gjvc 1 day ago

      by that time it's too late and should have been prevented appearing on the host much earlier

    • marcosdumay 1 day ago

      That's important only for the people that add relative names (like '.') to their path.

      Most people know better.

    • 3eb7988a1663 1 day ago

      All sorts of utilities push themselves to the front of path: uv, mise, asdf, python virtual environments, nix shell, etc.

      It is a theoretically nice ideal that fails immediately when you want project specific overrides.

      • jdxcode 1 day ago

        That’s not true with mise. You can add something to the front of PATH in front of mise bins and mise will respect that.

IshKebab 1 day ago

Bash footgun #238503.

  • _ZeD_ 1 day ago

    well.. it's in zsh (and probably any other *sh) too

    • vbernat 1 day ago

      In Zsh, you can set PATH with path=(~/.local/bin $path). There, it works as shell expansion works.

  • paulddraper 1 day ago

    That's standard POSIX.

    Tilde expands when at the beginning of an unquoted word.

    Pretty straightforward.

      ~ or ~/ --> $HOME
    
      ~user --> user's home
    

    ---

    Bash has a few extra.

      ~+ --> $PWD
    
      ~- --> $OLDPWD
    
      ~+N or ~-N --> dirs
    • Joel_Mckay 1 day ago

      In most use cases, the bash scripts location is more important than $HOME. This is because it is resilient to changes in user in the session, and parent process current working location contexts. =3

      scriptPath=$(/usr/bin/realpath "${BASH_SOURCE[0]}")

      localPath=$(/usr/bin/dirname "$scriptPath" )

      /usr/bin/echo "localPath = '$localPath'"

ChrisArchitect 1 day ago

the irony of the submitted url having a weird double slash // in it.

mr_mitm 1 day ago

So many headaches could be avoided if we only allowed `[A-Za-z0-9._-]` in paths. (Arguably, even `-` can be problematic.) Encoding issues, expansion, parameter separation, ... and I never saw a convincing case in favor of supporting anything else.

  • jetbalsa 1 day ago

    What about people who do not speak English? or even use Latin letters?

    • ThunderSizzle 1 day ago

      What Latin letter(s) are you referring to?

      The (classical) Latin alphabet can be fully described by the English alphabet.

      • cowboylowrez 1 day ago

        unicode names spit

        • jetbalsa 1 day ago

          if I want to name my file <turd emoji>.txt I damn well will

          • mnw21cam 1 day ago

            Any fule no that a file name is just an array of bytes (excluding the null byte and the slash). You can create a file with a name that is an invalid UTF-8 encoding. It's fun to see how much software chokes on that!

      • toast0 1 day ago

        What about people who do not ((speak English?) or (even use Latin letters?))

    • mr_mitm 1 day ago

      The language I grew up with does use non latin letters, I can manage. Then again, it's only four of them, so I get your point ... but I can dream, can't I?

  • dylan604 1 day ago

    I always joked about setting a custom keyboard layout to replace the space char with the underscore char specifically for avoiding spaces in file paths.

  • Dylan16807 1 day ago

    If you're trying to avoid headaches then definitely don't allow -. At least not as the first character in a segment.

    And allowing any letters from any language is probably worth the hassle.