How can pass the value of a variable to the standard input of a command?

SecurityBashStdin

Security Problem Overview


I'm writing a shell script that should be somewhat secure, i.e., does not pass secure data through parameters of commands and preferably does not use temporary files. How can I pass a variable to the standard input of a command?

Or, if it's not possible, how can I correctly use temporary files for such a task?

Security Solutions


Solution 1 - Security

Passing a value to standard input in Bash is as simple as:

your-command <<< "$your_variable"

Always make sure you put quotes around variable expressions!

> Be cautious, that this will probably work only in bash and will not work in sh.

Solution 2 - Security

Simple, but error-prone: using echo

Something as simple as this will do the trick:

echo "$blah" | my_cmd

Do note that this may not work correctly if $blah contains -n, -e, -E etc; or if it contains backslashes (bash's copy of echo preserves literal backslashes in absence of -e by default, but will treat them as escape sequences and replace them with corresponding characters even without -e if optional XSI extensions are enabled).

More sophisticated approach: using printf

printf '%s\n' "$blah" | my_cmd

This does not have the disadvantages listed above: all possible C strings (strings not containing NULs) are printed unchanged.

Solution 3 - Security

Note that the 'echo "$var" | command operations mean that standard input is limited to the line(s) echoed. If you also want the terminal to be connected, then you'll need to be fancier:

{ echo "$var"; cat - ; } | command

( echo "$var"; cat -   ) | command

This means that the first line(s) will be the contents of $var but the rest will come from cat reading its standard input. If the command does not do anything too fancy (try to turn on command line editing, or run like vim does) then it will be fine. Otherwise, you need to get really fancy - I think expect or one of its derivatives is likely to be appropriate.

The command line notations are practically identical - but the second semi-colon is necessary with the braces whereas it is not with parentheses.

Solution 4 - Security

(cat <<END
$passwd
END
) | command

The cat is not really needed, but it helps to structure the code better and allows you to use more commands in parentheses as input to your command.

Solution 5 - Security

This robust and portable way has already appeared in comments. It should be a standalone answer.

printf '%s' "$var" | my_cmd

or

printf '%s\n' "$var" | my_cmd

Notes:

  • It's better than echo, reasons are here: Why is printf better than echo?

  • printf "$var" is wrong. The first argument is format where various sequences like %s or \n are interpreted. To pass the variable right, it must not be interpreted as format.

  • Usually variables don't contain trailing newlines. The former command (with %s) passes the variable as it is. However tools that work with text may ignore or complain about an incomplete line (see Why should text files end with a newline?). So you may want the latter command (with %s\n) which appends a newline character to the content of the variable. Non-obvious facts:

  • Here string in Bash (<<<"$var" my_cmd) does append a newline.

  • Any method that appends a newline results in non-empty stdin of my_cmd, even if the variable is empty or undefined.

Solution 6 - Security

I liked Martin's answer, but it has some problems depending on what is in the variable. This

your-command <<< """$your_variable"""

is better if you variable contains " or !.

Solution 7 - Security

As per Martin's answer, there is a Bash feature called Here Strings (which itself is a variant of the more widely supported Here Documents feature):

> 3.6.7 Here Strings > > A variant of here documents, the format is: > > <<< word > > The word is expanded and supplied to the command on its standard > input.

Note that Here Strings would appear to be Bash-only, so, for improved portability, you'd probably be better off with the original Here Documents feature, as per PoltoS's answer:

( cat <<EOF
$variable
EOF
) | cmd

Or, a simpler variant of the above:

(cmd <<EOF
$variable
EOF
)

You can omit ( and ), unless you want to have this redirected further into other commands.

Solution 8 - Security

Try this:

echo "$variable" | command

Solution 9 - Security

If you came here from a duplicate, you are probably a beginner who tried to do something like

"$variable" >file

or

"$variable" | wc -l

where you obviously meant something like

echo "$variable" >file
echo "$variable" | wc -l

(Real beginners also forget the quotes; usually use quotes unless you have a specific reason to omit them, at least until you understand quoting.)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser283145View Question on Stackoverflow
Solution 1 - SecurityMartinView Answer on Stackoverflow
Solution 2 - SecurityOliver CharlesworthView Answer on Stackoverflow
Solution 3 - SecurityJonathan LefflerView Answer on Stackoverflow
Solution 4 - SecurityPoltoSView Answer on Stackoverflow
Solution 5 - SecurityKamil MaciorowskiView Answer on Stackoverflow
Solution 6 - SecurityRobert JacobsView Answer on Stackoverflow
Solution 7 - SecuritycnstView Answer on Stackoverflow
Solution 8 - SecurityunbeliView Answer on Stackoverflow
Solution 9 - SecuritytripleeeView Answer on Stackoverflow