1
#!/bin/sh
NR=0
getent passwd | while IFS=':' read USERNAME PSSWD MY_UID MYGID GECOS HOMEDIRS SHELLS
do
users[$NR]=$USERNAME
((NR++))
done
echo "${users[*]}"

if I execute above with ksh replacing sebang it works, but with bash it does not.

thor
  • 21,418
  • 31
  • 87
  • 173
shamimakhtar
  • 39
  • 1
  • 3
  • use `while ... done < <(getent passwd)` – Andrea Corbellini Apr 05 '16 at 07:27
  • I would have used `while ... done < /etc/passwd`, but the last comment of @Andrea Corbellini is better. For the ignorants like me that don't know this type of statement, look in the bash man page for `Process substitution` – oliv Apr 05 '16 at 07:50
  • If you've got Bash 4.2 or later, you can get the same behaviour as `ksh` by setting the `lastpipe` option: `shopt -s lastpipe`. – pjh Apr 05 '16 at 11:30

1 Answers1

1

Both sh and bash create a subshell after the pipe.

In other words, while block inherits the variables from the parent shell but modification won't affect the variables in the parent shell.

ksh is not creating a subshell, so it is working.

In my tests, the script provided does not run in pure sh.

A simple sh solution would be to use /etc/passwd directly:

#!/bin/sh --
NR=0
users=""
while IFS=':' read USERNAME PSSWD MY_UID MYGID GECOS HOMEDIRS SHELLS ; do 
  users="${users} ${USERNAME}" 
  NR=$(expr "${NR}" + 1) 
done < /etc/passwd
printf "%s\n" ${users} 

There is below a solution using bash (with @pjh hint):

#!/bin/bash --
NR=0
passwdentries="$(getent passwd)"
if [ $? -ne 0 ] ; then
    exit 1
fi
while IFS=':' read USERNAME PSSWD MY_UID MYGID GECOS HOMEDIRS SHELLS ; do 
   users[$NR]=$USERNAME 
   ((NR++)) 
done <<<"${passwdentries}"
printf "%s\n" ${users[*]} 

In order to illustrate this subshell situation, the version below prints all the users, but still the values of the variables are unchanged in parent shell (with @glennjackman hint):

#!/bin/bash --
NR=0
getent passwd | (
  while IFS=':' read USERNAME PSSWD MY_UID MYGID GECOS HOMEDIRS SHELLS ; do
    users[$NR]=$USERNAME
    ((NR++))
  done
  printf "%s\n" ${users[*]}
  )
Jay jargot
  • 2,745
  • 1
  • 11
  • 14
  • 1
    There is a very serious problem with process substitution: it's impossible to get the exit status of sub-processes. A useful alternative is to put the command output in a variable and then take input from the variable: `getent_output=$(getent passwd) ; IFS=: read ... ; do ... ; done <<< "$getent_output"`. – pjh Apr 05 '16 at 11:37
  • 1
    Nice. In the last one, the line continuations are not required: it's OK to have a multi-line script within parentheses. – glenn jackman Apr 05 '16 at 11:37
  • @pjh true, the answer had been updated, thx! – Jay jargot Apr 05 '16 at 12:02
  • @glennjackman true! the answer had been cleaned up, thx! – Jay jargot Apr 05 '16 at 12:02
  • @all : thanks for your answers. – shamimakhtar Apr 10 '16 at 09:35