简体   繁体   中英

How can I create a bash environment variable that prefixes an environment variable before a command?

I seem to be able to create environment variables that execute commands; like this:

$ cat ./src
FOO="echo"
$ . ./src
$ echo $FOO
echo
$ $FOO hello
hello
$

Is there a way I can modify that environment variable so that it prefixes the setting of another environment variable before the command? Ie is there a way to work around the following problem?

$ cat ./src
FOO="MY_DIR=/tmp echo"
$ . ./src
$ echo $FOO
MY_DIR=/tmp echo
$ $FOO hello
-bash: MY_DIR=/tmp: No such file or directory
$

Ie what I'd like to happen is to have an environment variable that does the equivalent of the following manually typed in the shell:

$ MY_DIR=/tmp echo hello
hello
$

...similar to how sans envvar-prefix, $FOO effectively had the same effect as typing echo at the shell.


/tmp/ exists of course, btw:

$ ls -ld /tmp/
drwxrwxrwt. 25 root root 500 May 19 11:35 /tmp/
$

Update:

I have a constraint that " FOO " must be invoked like $FOO hello and not FOO hello . So unfortunately a function like in @John Kugelman's (current) answer can't be a solution, even if it's more proper.

It's best to put data into variables, code into functions. Functions are more natural, expressive, and flexible than variables holding code. They look just like any other command but can take arbitrary actions, including but not limited to prepending commands and variable assignments.

foo() {
    MY_DIR=/tmp echo "$@"
}
foo hello

Here "$@" is a placeholder for the arguments passed to foo() .

I have a constraint that " FOO " must be invoked like $FOO hello and not FOO hello .

That constraint is impossible, I'm afraid.

I am curious about the mechanics of what's going on here: ie why can you make an environment variable that's sort of "aliased" to a command (I know true aliasing is something else), but that mechanism doesn't accommodate the seemingly small change to prefix "stuff" to the command?

Bash expands commands in several passes in a fixed, prescribed order. Very early on it splits the command into words and then marks the variable assignments with invisible flags. It expands $variable references in a later pass. It doesn't look at the results to see if they look like additional variable expansions. The equal signs are effectively ignored.

If you want to know the nitty gritty details, open up the Bash man page . It's incredibly long and the details are scattered throughout. Let me pull out the key sections and some choice quotes to help you digest it:

  1. Shell Grammar, Simple Commands

    A simple command is a sequence of optional variable assignments followed by blank -separated words and redirections, and terminated by a control operator .

  2. Simple Command Expansion

    When a simple command is executed, the shell performs the following expansions, assignments, and redirections, from left to right.

    1. The words that the parser has marked as variable assignments (those preceding the command name) and redirections are saved for later processing.

    2. The words that are not variable assignments or redirections are expanded. If any words remain after expansion, the first word is taken to be the name of the command and the remaining words are the arguments.

    ...

    If no command name results, the variable assignments affect the current shell environment. Otherwise, the variables are added to the environment of the executed command and do not affect the current shell environment.

  3. Expansion

    Expansion is performed on the command line after it has been split into words. There are seven kinds of expansion performed: brace expansion , tilde expansion , parameter and variable expansion , command substitution , arithmetic expansion , word splitting , and pathname expansion .

    The order of expansions is: brace expansion, tilde expansion, parameter, variable and arithmetic expansion and command substitution (done in a left-to-right fashion), word splitting, and pathname expansion.

  4. Expansion, Parameter Expansion

    The $ character introduces parameter expansion, command substitution, or arithmetic expansion.

Assignments are marked in step 1 and variables (AKA parameters) are expanded in step 4.

The only things that happen after variable expansion are:

  1. Word splitting. A variable can expand to multiple words if it contains whitespace. (Or to be more precise, if it contains any of the characters in the inter-field separator variable $IFS .)

  2. Pathname expansion. Also known as globbing, or wildcards. If a variable contains * , ? , or [ they'll be expanded to the names of matching files, if there are any.

  3. Quote removal. This pass happens after variable expansion, but it specifically does not apply to the results of any previous expansion step. So quotes the user typed are removed, but quotes that were the results of a substitution are retained.

Neither word splitting nor pathname expansion are what you need, so that's why it's not possible to store an assignment in a variable.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM