Not to conflict with any of the other pre-existing answers, but I thought a step-by-step analysis may help. (u1686_grawity's answer looks accurate, but I thought this might be more clear...)
# ensure TEST is unset
export TEST=''
Good news: This likely works as you expected. The # turns the first line into a comment, and then the next line makes the TEST variable have an empty string (zero bytes).
echo "Hello world!" > test.txt && TEST="$(cat test.txt)" echo "$TEST"
The shell notices the &&. It will run the first portion of the code...
echo "Hello world!" > test.txt
Then, it processes the &&. It looks at the return value of the echo command, and sees that it is zero. Therefore, it can proceed to run the second command. The second command is:
TEST="$(cat test.txt)" echo "$TEST"
So, the first thing the shell is going to do here is to replace the variables. The "command substitution" acts like a variable here, so the "cat text.txt" command runs. In the shell's perspective, the command now looks more like:
TEST="Hello world!" echo "$TEST"
Now, that all probably looks good so far, but here's the big issue. The shell doesn't start running the 'TEST="Hello world!"
' command yet. Instead, the shell notices that there is still a reference to $TEST
, so the shell replaces that.
Now, the command that the shell plans to run looks something like this:
TEST="Hello world!" echo ""
I suppose that if I'm going to go into some full detail, I will point out that the shell does perform the act of breaking this command into pieces. This action removes the quotation marks. So the individual pieces look like this:
-
TEST
-
=
-
Hello world!
-
echo
-
(empty value)
Okay, now the shell finally is done replacing things, so it can go onto the next task, which is to find what executable code to run.
It notices that the command line starts with a form of A=B C
(where A
represents a variable name, the equal sign specifies that a variable will be assigned a value, and B
represents the value to be assigned, and then C
is an optional portion which represents a command to run.
When the shell notices the command line fits this general pattern, the executable code that it is going to run is the code that handles the equal sign. Basically, the equal sign is similar to an executable filename, in the sense that the equal sign ends up controlling what code the shell is going to be executing. The shell proceeds to execute the code to assign a variable named TEST
to have a value of Hello world!
.
Then, after that is done, the shell will start the requested command, which uses the 4th and 5th potions identified above (where the 4th portion is the echo
command, and the 5th portion is the empty string).
The reason that i's answer (err... I mean, sqrt-1's answer) works is that the second && causes the shell to run the TEST="$(cat test.txt)"
code's return value is, notice that is zero, and only then proceed to process what comes after the second &&
(so then the echo "$TEST"
portion will have the $TEST
variable processed, and by now it all works).
Note that sqrt-1's provided answer shows some code that is a little bit wasteful, although understandably so because it does directly answer the asked question. What it basically does is:
First, splits the command into three parts:
echo "Hello world!" > test.txt
TEST="$(cat test.txt)"
echo "$TEST"
The third part only runs if the second part (runs and) succeeds, and the second part only runs if the first command succeeds, because of the two &&
operators.
Another variation could be:
echo "Hello world!" > test.txt && ( TEST="$(cat test.txt)" ; echo "$TEST" )
With that semi-colon, the results of the 'TEST="$(cat test.txt)"
' will not be evaluated to determine whether the 'echo "$TEST"' will be run. When I see a &&
, I tend to try to figure out what is going to be evaluated, and why that would matter. So, despite the added complexity of needing to use parenthesis, this variation with a semi-colon actually seems to me like it may be a bit simpler to mentally process.
ANSIBLE_VAULT_PASSWORD_FILE
. This will be likeANSIBLE_VAULT_PASSWORD_FILE=/path/to/file ansible-playbook …
where/path/to/file
is an exectuable script that will rungpg -d ~/gpg_encrypted_vault_password_file 2>/dev/null
. Not tested because I have not and I do not useansible-playbook
, I have just skimmed through the documentation./proc/
file system. A password passed this way is just as exposed as a if it were stored in a plaintext file with owner-only read permissions.