The content of the USERS-File is like this:
user1
user2
user3
The Script is like this:
USERSFILE="/tmp/users.txt"
for USER in <`$USERSFILE`; do
...
...
eval echo LOGGEDIN_${USER}=OK
done
for USER in <`$USERSFILE`; do
if ! -z $(eval echo "\$LOGGEDIN_\$USER") ]; then
[INDENT]echo "something"[/INDENT]
else
[INDENT]echo "something else"[/INDENT]
fi
done
My problem is, that I cannot verify the content of the variable $LOGGEDIN_${USER} (if it’s zero or not), because the “LOGGEDIN”-Variable will not be resolved.
I do notexactly understand what you want to do, but the command
for USER in <`$USERSFILE` ; do
...
done
I do not understand at all.
First it $USERFILE is replaced by the value /tmp/users.txt so the whole then is:
for USER in <`/tmp/users.txt` ; do
...
done
Then the quotes let /temp/users.txt execute, but it is no executable script or program!
AND my bash complains against the < because no redirection is possible in this context:
./hh: line 5: syntax error near unexpected token `<'
./hh: line 5: `for USER in <`$USERSFILE`; do'
When I understand correct what you want to do, I would do like
while read USER ; do
....
done <${USERFILE}
So I am not even reaching to your eval.
But that looks strange also. When the eval is evaluated the reslult is een echo command:
echo LOGGEDIN_user1=OK
which means that this text is output to stdout. There wiill be no variable asignment. If that is what you mean just write
As you can see, there’s no query to my first defined Variable "LOGGEDIN_${USER}=“OK”. The word “LOGGEDIN” (in the 2nd line of the code) is not appended. I think, that’s the reason, why I can’t query, if the variable has a zero length or not.
This is an interesting question, because what you are basically asking for is an associative array feature in bash. Unfortunately this is not a current feature in bash although it has been proposed. If you search you will find various articles suggesting techniques for faking this feature, e.g.
However if I were in your place, I would rewrite it in any language that supports associative arrays, e.g. awk, perl, python, you name it. But that didn’t really answer your question.
Such a think should be buildup piece by piece else nobody does understand how to do it. Below I give you everytime a listing of my tiny script and what it prints. My first version:
henk@boven:~> cat hh
#!/bin/bash
LOGGEDIN_user1="OK"
USER="user1"
if $(eval echo "\${LOGGEDIN_${USER}}") = OK ]]
then echo "It is OK"
else echo "It is NOT ok"
fi
henk@boven:~> ./hh
It is OK
henk@boven:~>
LOGFILE="/var/log/user.log"
USERS="/etc/appl/users"
NOW_MONTH=`date -d "0 month ago" +%b`
ONE_MONTH_BEFORE=`date -d "1 month ago" +%b`
TWO_MONTH_BEFORE=`date -d "2 month ago" +%b`
THREE_MONTH_BEFORE=`date -d "3 month ago" +%b`
ARRAY_USERS=(`cat $USERS`)
check_login()
{
for USER in ${ARRAY_USERS[@]}; do
for MONTH in ${ARRAY_ALL_MONTH[li]}; do
[/li] cat $LOGFILE | grep -i $MONTH | grep -i $USER >/dev/null 2>&1
if [ "$?" = "0" ]; then
eval echo LOGGEDIN_$USER=OK
else
echo "User $USER has never logged in during $MONTH." >/dev/null 2>&1
fi
done
echo -e "
"
done
}
print_message()
{
for USER in ${ARRAY_USERS[@]}; do
if [[ $(eval echo "\${LOGGEDIN_${USER}}") = OK ]]; then
echo -e "User \"$USER\" is OK."
else
echo -e "User \"$USER\" can be deleted."
fi
done
}
### Main
check_login
print_message
And this is the output of the print_message()-function:
print_message
for USER in ‘${ARRAY_USERS[@]}’
++ eval echo ‘${LOGGEDIN_user1}’
+++ echo
[[ ‘’ = OK ]]
echo -e ‘User “user1” can be deleted.’
User “user1” can be deleted.
for USER in ‘${ARRAY_USERS[@]}’
++ eval echo ‘${LOGGEDIN_user2}’
+++ echo
[[ ‘’ = OK ]]
echo -e ‘User “user2” can be deleted.’
User “user2” can be deleted.
As you can see, the value “ok” - set in the first function - will not be interpreted.
Any hints?
I see you understood the long post I gave you earlier. Glad you did because it took me some time
Now the same principles on the next one:
eval echo LOGGEDIN_$USER=OK
is wrong because you do not want an echo statement to be executed and after the eval this is what is presented again to bash:
echo LOGGEDIN_user1=OK
And that just prints
LOGGEDIN_user1=OK
to stdout.
The following is what you need:
eval LOGGEDIN_$USER=OK
becuase after the eval it will be
LOGGEDIN_user1=OK
and that is what you want to happen.
BTW I prefer to use ${…} always. It may be a bit more typing, but in the more complicated situations (as those we are in now) it improves so much the reading and the error detection. For the same reason I always use $(…) instead of the, very old fashioned and error prone, ....
Thanks a lot for your answer. It solved my problem with 50%
When the user-variable only contains “user1”, all works fine. But when the user-variable contains a value like this
“abc-efg-user1”, then I receive again an error:
eval LOGGEDIN_abc-efg-user1=ok
-bash: LOGGEDIN_abc-def-user1=ok: command not found
How can I set this value? I tried with escaping the special characters…but no success.
Thanks a lot.
Tom
I am afraid this can not be solved in the way we are working now because it always ends up in a asignment to a variable name that isn’t a variable name.
A-B="something"
is always wrong in bash (and in other shells).
When you do not mind (and it is allright with the rest of your script) you could change all - in _ . Either by doing something like
USC_USER=$(echo ${USER} | sed 's/-/_/g')
for each individual user within the loop (and that twice I think coz you have two loops, thus a lot of calls to sed). You have than USER and USC_USER available.
I would prefer a solution that I showed earlier. Making one big loop:
cat ${USERS} | sed ‘s/-/_/g’ | while read USER
do
…
done
But then you would not have the original version (with the - ) available within the script which you may need.
I hope with these suggestions you may compose a solution.
But I repeat: Blweh. You should revise your username policy. I hope there are not more ‘strange’ characters to be found in them.
The account name must begin with an alphabetic character and the rest of the string should be from the POSIX portable character class ([A-Za-z_][A-Za-z0-9_-.]*[A-Za-z0-9_-.$]).
So they are allowed, but not all that is allowed is sound practice.
I will not take over the job of writing your script but as the usage of the username as part of a variable name can give problems (because both have different definitions about the allowed characters) may be another approach is better.
What about two arrays. One with the username and one with the ok. Initialize a counter
integer I=0
and add 1 every cycle
USERARR*="${USER}"
USERSTAT*="OK"
(( I += 1 ))
The second loop should then count from 0 to I and use the values.
Strange, I have uppercase I between the ] and this forums makes lower case i out off them >:)
This would be a primitive sort of the associative array ken_yap mentioned earlier. Just a suggestion.**
I seems, that our usernames are really not posix-compatible. So I changed the usernames to contain a “_” instead of a “-”. Now the script works really fine.
Thanks a lot for your support.