简体   繁体   中英

Bash- passing input without shell interpreting parameter expansion chars

So I have a script where I type the script.sh followed by input for a set of if-else statements. Like this:

    script.sh fnSw38h$?2

The output echoes out the input in the end.

But I noticed that $? is interpreted as 0/1 so the output would echo:

    fnSw38h12

How can I stop the shell from expanding the characters and take it face value?

I looked at something like opt noglob or something similar but they didn't work. When I put it like this:

    script.sh 'fnSw38h$?2'

it works. But how do I capture that within single quotes ('') when I can't state variables inside it like Var='$1'

Please help!

How to pass a password to a script

I gather from the comments that the true purpose of this script is to validate a password. If this is an important or sensitive application, you really should be using professional security tools. If this application is not sensitive or this is just a learning exercise, then read on for a first introduction to the issues.

First, do not do this:

script.sh fnSw38h$?2

This password will appear in ps and be visible to any user on the system in plain text .

Instead, have the user type the password as input to the script, such as:

#!/bin/sh
IFS= read -r var

Here, read will gather input from the keyboard free from shell interference and it will not appear in ps output.

var will have the password for you to verify but you really shouldn't have plain text passwords saved anywhere for you to verify against. It is much better to put the password through a one-way hash and then compare the hash with something that you have saved in a file. For example:

var=$(head -n1 | md5sum)

Here, head will read one line (the password) and pass it to md5sum which will convert it to a hash. This hash can be compared with the known correct hash for this user's password. The text returned by head will be exactly what the user typed, unmangled by the shell.

Actually, for a known hash algorithm, it is possible to make a reverse look-up table for common passwords. So, the solution is to create a variable, called salt , that has some user dependent information:

var=$( { head -n1; echo "$salt"; } | md5sum)

The salt does not have to be kept secret. It is just there to make look-up tables more difficult to compute.

The md5sum algorithm, however, has been found to have some weaknesses. So, it should be replaced with more recent hash algorithms. As I write, that would probably be a sha-2 variant.

Again, if this is a sensitive application, do not use home-made tools

Answer to original question

how do I capture that within single quotes ('') when I can't state variables inside it like Var='$1'

The answer is that you don't need to. Consider, for example, this script:

#!/bin/sh
var=$1
echo $var

First, note that $$ and $? are both shell variables:

$ echo $$ $? 
28712 0

Now, let's try our script:

$ bash ./script.sh '$$ $?'
$$ $?

These variables were not expanded because (1) when they appeared on the command line, they were in single-quotes, and (2) in the script, they were assigned to variables and bash does not expand variables recursively . In other words, on the line echo $var , bash will expand $var to get $$ $? but there it stops. It does not expand what was in var .

You can escape any dollar signs in a double-quoted string that are not meant to introduce a parameter expansion.

var=foo
# Pass the literal string fnSw38h$?2foo to script.sh
script.sh "fnSw38h\$?2$var"

You cannot do what you are trying to do. What is entered on the command line (such as the arguments to your script) must be in shell syntax, and will be interpreted by the shell (according to the shell's rules) before being handed to your script.

When someone runs the command script.sh fnSw38h$?2 , the shell parses the argument as the text "fnSw38h", followed by $? which means "substitute the exit status of the last command here", followed by "2". So the shell does as it's been told, it substitutes the exit status of the last command, then hands the result of that to your script.

Your script never receives "fnSw38h$?2", and cannot recover the argument in that form. It receives something like "fnSw38h02" or "fnSw38h12", because that's what the user asked the shell to pass it . That might not be what the user wanted to pass it, but as I said, the command must be in shell syntax, and in shell syntax an unescaped and unnquoted $? means "substitute the last exit status here".

If the user wants to pass "$?" as part of the argument, they must escape or single-quote it on the command line. Period.

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