-1

Consider the following example:

file="test" echo $file

My idea is that file persists only during the command echo $file. Although $file does not persist after this command, echo prints nothing, as if $file were not set.

Is it possible to set one-command-lifelong variables in the shell?

edit: I don't want to resort to shell scripts or functions as suggested in all these related questions

  • Relating (duplicate?): https://unix.stackexchange.com/q/628231/117549 – Jeff Schaller Feb 02 '24 at 15:49
  • @JeffSchaller It is related but not duplicated, at all. He/She looks for persistent variable assignment during command execution. I look for nonpersistent variable assignment during command execution. – Rubem Pacelli Feb 02 '24 at 16:09
  • 2
    The linked Q&A does explain what’s happening in your test: file is set only for echo, but $file is expanded before echo runs (it has to be) so $file is empty when it’s expanded. Try the example given in the linked answer, using a shell script: you’ll see that the variable is indeed set for the command (in a non-persistent manner). – Stephen Kitt Feb 02 '24 at 16:18
  • 1
  • "The linked Q&A does explain what’s happening in your test". I am not asking "What happens with file?". "Try the example given in the linked answer, using a shell script", my idea is to not resort to shell scripts. I have a answer for my own question (none of the aforementioned Q&A answered it). Open it so that I can answer it. – Rubem Pacelli Feb 02 '24 at 16:24
  • 1
    The thing is, your command here, file="test" echo $file does exactly what you want it to do. Can you edit and clarify the use case? Do you really want the variable to only be available in the command's environment, or do you want it to be available in the parent (which is what echo "$var" requires) but to then be unset after the command finishes? If you only want it to exist in the command's environment, "for the lifetime of the command" then var=foo command is the right way. – terdon Feb 02 '24 at 16:57
  • Just to throw a more gasoline on the fire, part of the problem with your test case, is that echo doesn't care about any variables in the environment, (except when they are embedded in the cmd line arguments or as otherwise evaluated by the cmd-processing loop). But take the case of a program that has its own env var, less, If you have an env var export LESS=-CQaix4, you can override that (just the one time) with LESS= less /path/to/some/file. When you open the file all of the features from -CQaix4 are gone. less is always looking in its environment for $LESS. (just for example). – shellter Feb 03 '24 at 04:40
  • OP's preferred version is given In https://unix.stackexchange.com/a/56454/70524: "Portably (Bourne and POSIX): (var=value; echo "1: $var"); echo "2: $var" The (...) above starts a sub-shell". – muru Feb 04 '24 at 13:11

2 Answers2

1

The command

file="test" echo $file

doesn't output test because the expansion occurs before running the command. Check this out:

$ set -x
$ foo='hello' echo $foo
+ foo=hello
+ echo

The same thing doesn't occur when you run a Unix shell script instead of running a shell command, e.g.,

foo='hello' /path/to/script.sh

In this case, the foo='hello' assignment is first resolved, then /path/to/script.sh is run.

Some previous and related posts proposed solutions for persistent variable assignment + CLI commands:

  1. foo='hello' bash -c 'echo $foo': Using shell script is not desirable! Wordy approach to simple commands.
  2. (){ local var=value; echo "1: $var"; }; echo "2: $var": Using functions is not desirable! Wordy approach to simple commands.
  3. TEST=foo && echo $TEST: Simple but persistent.

My way out is

(file="test"; echo $file)

By creating a subshell, you can create a shell variable, use it in the echo command, and then it won't persist outside this command.

muru
  • 72,889
  • Can you explain why you consider examples 1 and 2 as creating “persistent” variables? If foo is initially unset, after foo=hello bash -c 'echo $foo' it remains unset. – Stephen Kitt Feb 02 '24 at 16:33
  • @StephenKitt I edited to make clearer which solutions make persistent environment variables. – Rubem Pacelli Feb 02 '24 at 16:37
  • 1
    I don't understand this. var=value command is exactly what you want: it sets the variable var to value only for the execution environment of command. Your solution here actually does make it persist outside, it's just that you are adding the extra step of running a subshell and the variable is set only in the subshell. So once the subshell exits, you no longer have the variable set, but that isn't what your question asked for. – terdon Feb 02 '24 at 16:55
  • file='test' eval 'echo $test' has the advantage that it does not create an extra shell. – NickD Feb 02 '24 at 17:06
  • (){ local var=value; echo "1: $var"; } same as function { local var=value; echo "1: $var"; } is zsh syntax for an anonymous function. See also var=value command eval 'echo "$var"' which works in many shells. – Stéphane Chazelas Feb 02 '24 at 17:20
  • re. "The same thing doesn't occur when you run a Unix shell script instead running a shell command, e.g.,", well, that's not true, e.g. try foo='hello' /path/to/script.sh "$foo" with some script that uses "$1". Same with foo=bar sh -c 'echo ">$1<"' sh "$foo". – ilkkachu Feb 02 '24 at 18:25
  • It's also a bit unclear why you say "Using shell script is not desirable! " and "Using functions is not desirable!" (esp. with exclamation marks) as the first comment about those. Is it that you just want axiomatically avoid scripts or functions, or is it that you see some downsides in those solutions? If the latter, which downsides? – ilkkachu Feb 02 '24 at 18:27
  • 1
    @terdon, well, in e.g. (foo=xyz; echo "$foo"); echo "$foo" the modified value of foo effectively persists only for the duration of the first echo (including the expansions in that command), not the rest of the script (the second echo), so IMO, it looks to fill their desire exactly. Too bad something like foo=xyz { whatever... } doesn't work. – ilkkachu Feb 02 '24 at 18:29
  • re. TEST=foo && echo $TEST, this is same as just TEST=foo; echo $TEST, and would be clearer written that way. That is, unless you have a command substitution on the right-hand side of the assignment (TEST=$(foo) && echo $TEST), in which the && would test the result of that, which may or may not be what you want. – ilkkachu Feb 02 '24 at 18:34
1

The variable binding in your command line doesn't take place until the command line is executed. But the parameter substitution of $file takes place while the command is expanded, which must happen before it is executed.

The way around this is to put your command into a function:

fun()
{
   echo "file = " $file
}

file=abc fun

Kaz
  • 8,273