5

i have a program which gives out an environement variable

TIME=1328189073
CLIENT[if-modified-since]=Thu, 02 Feb 2012 12:09:40 GMT
HTTP_FILE=/news/rss.xml?edition=uk
HTTP_PORT=80
HTTP_HOST=feeds.bbci.co.uk
HTTP_PROTO=http
CLIENT[host]=feeds.bbci.co.uk
CLIENTID=10
CLIENT[user-agent]=Safari
PWD=/
VERSION=SR.4.2.2.MR.20110523
CLIENT[accept]=text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
SHLVL=1
CLIENT[accept-language]=en-gb,en;q=0.5
INTERFACE=192.168.221.196
CLIENT[cache-control]=max-age=0
CLIENT[accept-encoding]=gzip, deflate
HTTP_METHOD=GET
CLIENT[user-agent]

however when i try to access one of this variable from a bash script it gives no result

echo ${CLIENT[user-agent]} >> ${LOG}

however this works

echo ${TIME} ${CLIENTID} ${USERNAME} ${IP} ${HTTP_METHOD} ${HTTP_PROTO} ${HTTP_HOST} ${HTTP_PORT} ${HTTP_FILE} ${SIZE} >> ${LOG}

any idea why the user-agent fails to show?

5
  • @FatalError - Actually there are two lines with this string. The first one assigns a value, but the second one lacks the assignment operator, so it should do nothing. Commented Feb 3, 2012 at 17:53
  • 3
    The brackets [] seem to cause the problem. Do you really need them in the name? I don't think Bash supports arrays with a non-numeric index. Commented Feb 3, 2012 at 18:02
  • yeah the environment variables are given out by the proxy, so it has the brackets..i can read all other non-bracket fields fine.. Commented Feb 3, 2012 at 18:05
  • 1
    What do you mean exactly with "given out by the proxy"? Does the proxy run bash for you? Does it assign to the environment? Or does it return a buch of text? Commented Feb 4, 2012 at 10:31
  • You can either declare -A CLIENT to make that an associative array, or you can, say, s/[[]]/_/ on each variable name before reading it, so the shell treats everything as normal variables. Try using declare -P on your variables, especially CLIENT; it's always enlightening. Note that associative arrays are a feature of bash 4 and up. Commented Feb 19 at 3:38

6 Answers 6

3

You need to source your script instead of running it.

. set-vars-script.sh
Sign up to request clarification or add additional context in comments.

2 Comments

source set-vars-script.sh does the job too. But it requires less keyboard typing using a dot :-)
this didnt work, the scenario is that the proxy dumps all these environment variables and i am trying to read them..
3

You can not use braces in form ${VAR[ARITHM_EXPR]}. But you can always extract value of these variables to variables with VALID names:

  $ set | sed -n '/CLIENT\[user-agent]=/{s|.*=||;p;q;}'

Why you don't use Perl/Python for scripting? This resolve your problem:

  import os
  print(os.environ['CLIENT[user-agent]'])

Comments

1

Probably you should declare the array CLIENT before assigning to its elements:

declare -A CLIENT

Comments

0

Can you edit the script assigning all the values to the variables marked for export to subshells? Can you create a temporary file. Bash can't export arrays, associative or indexed; but you can use the BASH_ENV variable to name a set-up file for non-interactive subshells. Be sure to use declare -A CLIENT before assigning values. Then add something like this after all assignments to the CLIENT array

    declare -A -p CLIENT > /tmp/bash_env_tmp
    export BASH_ENV=/tmp/bash_env_tmp

A new non-interactive subshell will read and execute the file before processing its commands. If the shell is invoked with the -c option, then you can use a command substitution with on the line calling the subshell to supply the output of the same declare operation as the first commands in the new shell.

Comments

0

Direct answer: bash doesn't consider CLIENT[if-modified-since] a valid variable name, so you need to use something else to get its value. Fortunately, printenv can do the job:

printenv 'CLIENT[user-agent]' >> "${LOG}"

Note that this doesn't use the ${...}; that's bash's variable syntax. Also, it's a really good idea to quote the name (either single- or double-quotes would work), since square brackets are filename wildcard characters, and using those without proper quotes can lead to weird problems.

If you want to pass the value as an argument to another command, use printenv in a command substitution:

someCommand "$(printenv 'CLIENT[user-agent]')"

Note that you should almost always double-quote variable references, command substitutions, etc. I recommend running your scripts through shellcheck, and following its recommendations to improve your scripts in this, and a variety of other ways.

Another option is to copy the value to a bash variable and use that:

CLIENT_user_agent="$(printenv 'CLIENT[user-agent]')"
someCommand "$CLIENT_user_agent"

Explanation and clarification: I'm seeing a lot of confusion in the comments and other answers about what's going on here. The source of the confusion is that in bash syntax, CLIENT[user-agent] (used in a variable-name-like context) would refer to an array named CLIENT, indexed by user-agent (which itself might be either a string or a math expression, user minus agent, depending on whether CLIENT is an associative array or numeric-indexed array).

But, at least if I understand the situation correctly, the [user-agent] is just part of the environment variable name itself. At least in Linux, environment variable names can contain pretty much any characters except "=".

bash, on the other hand, only allows letters, numbers, and underscores in names, so it cannot (directly) set or access environment variables with names containing other characters.

Also, environment variables cannot be arrays, only plain strings. Arrays are a bash (and ksh and zsh and...) thing, not something the operating system's environment supports.

Comments

0

The notation:

 CLIENT[text]=Othertext

is used to set an index (text) in an associative array (CLIENT[]), and you can only do that in bash after you have used declare to tell the shell that the array is associative. Without that, an array is normal, and can only have integer indices.

So, you say you have a program which "gives out" environment variables. I'm assuming the variables are stored in a file, so you might:

declare -A CLIENT
source /path/to/variablefile
declare -p CLIENT
# or
echo ${CLIENT[user-agent]}

The first declare makes the associative array. You then source the variable file to do the assignments in the current shell rather than a subshell which will be lost when it finishes. Everything else is for demonstration purposes.


UPDATE

Though, come to think of it, this won't quite work as-is. Any line that tries to assign content but has a space in it will break. So:

CLIENT[if-modified-since]=Thu, 02 Feb 2012 12:09:40 GMT

is illegal. You can assign the variable correctly if you quote the data, of course. But these variables are created by a program, you said, and we're not here to debug that program. So,

declare -A CLIENT
sed -i 's/\([^=]*\)=\(.*\)/\1="\2"/' /path/to/variablefile
source /path/to/variablefile

The sed script simply quotes the variable assignments in the file. This seems to work on the file you provided. Quotes are good.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.