Should I use quotes in environment path names?

前端 未结 3 444
迷失自我
迷失自我 2021-01-30 16:53

I\'m in the process of cleaning up all my config files in an attempt to make them as readable as possible. I\'ve been looking for a style guide on the use of quotes while export

3条回答
  •  名媛妹妹
    2021-01-30 17:39

    Tip of the hat to @gniourf_gniourf and @chepner for their help.

    tl;dr

    To be safe, double-quote: it'll work in all cases, across all POSIX-like shells.

    If you want to add a ~-based path, selectively leave the ~/ unquoted to ensure that ~ is expanded; e.g.: export PATH=~/"bin:$PATH". See below for the rules of ~ expansion in variable assignments.
    Alternatively, simply use $HOME inside a single, double-quoted string:
    export PATH="$HOME/bin:$PATH"


    NOTE: The following applies to bash, ksh, and zsh, but NOT to (mostly) strictly POSIX compliant shells such as dash; thus, when you target /bin/sh, you MUST double-quote the RHS of export.[1]

    • Double-quotes are optional, ONLY IF the literal part of your RHS (the value to assign) contains neither whitespace nor other shell metacharacters.
    • Whether the values of the variables referenced contain whitespace/metacharacters or not does not matter - see below.
      • Again: It does matter with sh, when export is used, so always double-quote there.

    The reason you can get away without double-quoting in this case is that variable-assignment statements in POSIX-like shells interpret their RHS differently than arguments passed to commands, as described in section 2.9.1 of the POSIX spec:

    • Specifically, even though initial word-splitting is performed, it is only applied to the unexpanded (raw) RHS (that's why you do need quoting with whitespace/metacharacters in literals), and not to its results.

    • This only applies to genuine assignment statements of the form
      = in all POSIX-like shells
      , i.e., if there is no command name before the variable name; note that that includes assignments prepended to a command to define ad-hoc environment variables for it, e.g., foo=$bar cmd ....

    • Assignments in the context of other commands should always be double-quoted, to be safe:

      • With sh (in a (mostly) strictly POSIX-compliant shell such as dash) an assignment with export is treated as a regular command, and the foo=$bar part is treated as the 1st argument to the export builtin and therefore treated as usual (subject to word-splitting of the result, too).
        (POSIX doesn't specify any other commands involving (explicit) variable-assignment; declare, typeset, and local are nonstandard extensions).

      • bash, ksh, zsh, in an understandable deviation from POSIX, extend the assignment logic to export foo=$bar and typeset/declare/local foo=$bar as well. In other words: in bash, ksh, zsh, export/typeset/declare/local commands are treated like assignments, so that quoting isn't strictly necessary.

        • Perhaps surprisingly, dash, which also chose to implement the non-POSIX local builtin[2] , does NOT extend assignment logic to it; it is consistent with its export behavior, however.
      • Assignments passed to env (e.g., env foo=$bar cmd ...) are also subject to expansion as a command argument and therefore need double-quoting - except in zsh.

        • That env acts differently from export in ksh and bash in that regard is due to the fact that env is an external utility, whereas export is a shell builtin.
          (zsh's behavior fundamentally differs from that of the other shells when it comes to unquoted variable references).
    • Tilde (~) expansion happens as follows in genuine assignment statements:

      • In addition to the ~ needing to be unquoted, as usual, it is also only applied:
        • If the entire RHS is ~; e.g.:
          • foo=~ # same as: foo="$HOME"
        • Otherwise: only if both of the following conditions are met:
          • if ~ starts the string or is preceded by an unquoted :
          • if ~ is followed by an unquoted /.
          • e.g.,
            foo=~/bin # same as foo="$HOME/bin"
            foo=$foo:~/bin # same as foo="$foo:$HOME/bin"

    Example

    This example demonstrates that in bash, ksh, and zsh you can get away without double-quoting, even when using export, but I do not recommend it.

    #!/usr/bin/env bash
    # or ksh or zsh - but NOT /bin/sh!
    
    # Create env. variable with whitespace and other shell metacharacters
    export FOO="b:c &|<> d"
    
    # Extend the value - the double quotes here are optional, but ONLY 
    # because the literal part, 'a:`, contains no whitespace or other shell metacharacters.
    # To be safe, DO double-quote the RHS.
    export FOO=a:$foo # OK - $FOO now contains 'a:b:c &|<> d'
    

    [1] As @gniourf_gniourf points out: Use of export to modify the value of PATH is optional, because once a variable is marked as exported, you can use a regular assignment (PATH=...) to change its value.
    That said, you may still choose to use export, so as to make it explicit that the variable being modified is exported.

    [2] @gniourf_gniourf states that a future version of the POSIX standard may introduce the local builtin.

提交回复
热议问题