Using the following code, I'm trying to read a file into an array:
GROUPS=()
while IFS=: read -r g1 g2 g3 g4
do
GROUPS+=("$g3")
echo "$g3"
done < /etc/group
This doesn't work, it doesn't even output anything, however, if I leave only the echo it does print the contents of the file. As long as the instruction to add to an array is present somewhere in the loop, nothing is printed. It seems very strange since I can do this to other files without problems.
Any idea what's causing this? I checked with bash -x and when the problematic instruction is present, it doesn't even enter the loop.
Also, in case it is relevant, I'm running this as root.
-
It is generally advisable to make it a habit to use all-lowercase or mixed-case variable names in Bash in order to avoid potential name collisions with shell and environment variables.Dennis Williamson– Dennis Williamson2016年06月29日 21:46:45 +00:00Commented Jun 29, 2016 at 21:46
2 Answers 2
You unluckily selected name of the array which is already reserved by the bash
itself and is read only, so you cannot change it.
GROUPS
An array variable containing the list of groups of which the current user is a member. Assignments to GROUPS have no effect and return an error status. If GROUPS is unset, it loses its special properties, even if it is subsequently reset.
Just use another name and the code should work.
-
Why echo command does not print anything?Vombat– Vombat2016年06月29日 19:34:36 +00:00Commented Jun 29, 2016 at 19:34
-
@coffeMug That's a little mystery to me. It seems that trying to override GROUPS kills the rest of the entire command line:
GROUPS=(); echo test
gives nothing and exit code 1. SimilarlyGROUPS=("$(echo test >/dev/tty)")
jimmij– jimmij2016年06月29日 19:48:06 +00:00Commented Jun 29, 2016 at 19:48 -
@coffeeMug - as with the OP's symptom "it doesn't even enter the loop", I see it exiting the while loop with RC=1 after the first attempted += assignment, so the echo is not even executed. See more by putting the commands in a file and running it with
bash -x thatfile
2016年06月29日 20:23:02 +00:00Commented Jun 29, 2016 at 20:23
You already have an answer saying how to do this entirely in bash with a while ... read ...
loop...I'd like to suggest that doing it in bash alone is one of the worst ways to do this.
Also, reading /etc/group
itself is not a good idea because that file is just one of the possible sources of group data (other sources may include LDAP, Active Directory, winbind, NIS, mysql, pgsql, Berkeley db files, extra group files from libnss-extrausers
, and many more).
Instead, use the output of getent group
.
Try this:
g=( $(getent group | awk -F: '{print 3ドル}') )
or
g=( $(getent group | cut -d: -f3) )
The awk
method is particularly useful if you want to combine it with regexp or fixed-text search and/or other selection criteria (e.g. 1ドル == groupname
or 3ドル >= 500 && 3ドル <= 1000
)
BTW for, e.g., debugging you can print the groups in array g
with, e.g.:
# all on one line
echo "${g[@]}"
# one element per line
printf "%s\n" "${g[@]}"
# in a format that can be re-used to define the variable in another
# bash shell or script.
declare -p g
I particularly like declare -p
for debugging - it not only shows you the variable name (e.g. similar to echo "foo='$foo'"
), it also shows the array indices for array variables, and everything is properly quoted and backslash-escaped:
$ g=( $(getent group | awk -F: '1ドル ~ /my/ {print 1ドル}') )
$ declare -p g
declare -a g='([0]="mysql" [1]="mythtv")'
BTW typeset
is a synonym for declare
that works in bash and also some other shells like ksh.