I don't understand why my script is leaving the loop after the first line. It seems it breaks because i'm calling another script in deleteUser function.

Here is an extract of my script :


function deleteUser
  nfsserver="/local/scripts/find_home_in_nfs.sh ${1}"
  ssh -n $nfsserver "rm -rf /home/${1}"
  ssh ldap "sudo deleteUser ${1}"
  userHome ${1}

cat myFile | while read line
  username=`echo ${line} | awk -F" " '{print $1}'`
  action=`echo ${line} | awk -F" " '{print $2}'`

  if [ "${action}" == "d" ]; then
     deleteUser ${username}
  elif [ "${action}" == "a" ]; then
     addUser ${username}

I tried to rewrite a simplified version of my script without calling "find_home_in_nfs.sh" (that does a lot of ssh connection to all NFS servers to find home location), and it works.

But I have to use it and I can't modify it. Is there a way to make this while loop works ?

  • 2
    Why did you think you only needed -n on one of them? – Ignacio Vazquez-Abrams Nov 2 '17 at 18:36
  • 2
    I know shells are bad at looping over lines from a file (the missing ultimate newline problem) but what is that cat for? – thrig Nov 2 '17 at 18:43

You can simplify your loop considerably as shown in the following example:

while read username action
   if [ "${action}" == "d" ]; then
      echo "deleteUser ${username}"
   elif [ "${action}" == "a" ]; then
      echo "addUser ${username}"
done < myFile
  • 1
    ... and even more with case "$action" in d) ... ;; a) ... esac. – Kusalananda Nov 2 '17 at 20:24

The basic problem is that ssh is reading most of the file. read line reads the first line from the file, then it runs (among other things) ssh -n $nfsserver "rm -rf /home/${1}" which (because of the -n) does not read from standard input, then it runs ssh ldap "sudo deleteUser ${1}" which doesn't have -n and as a result reads in everything it can (i.e. the entire rest of the file) and sends it to the remote computer, which ignores it. Then, the next iteration starts, read line tries to read from the file but it's at the end so it exits the loop.

You could fix this by adding -n to that other ssh command (and any other ssh commands that might be run), and adding something like </dev/null to every other command that might possibly read from stdin... but I prefer to sent the file to be read over something other than stdin, which skips the whole mess. Here's how to send and read it using FD #3:

while read line <&3; do
done 3<myFile

Combining this with @fpmurphy1's suggestion of letting read handle the field splitting and @Kusalananda's of using case gives:

while read username action <&3
   case "$action" in
       d) deleteUser "$username" ;;
       a) addUser "$username" ;;
       *) echo "Unknown action: $action on user $username" >&2 ;;
done 3< myFile

BTW, if you're going to be adding and deleting a lot of users at a time, I'd be tempted to try to optimize it further. In the current version it opens a separate ssh session with each server for each user. It'd be faster to open a single connection to each server and do all users across that one connection. Downside: error handling becomes more complex if you aren't careful.


Simpler code, using a bash associative array:

declare -A a=([a]=add [d]=delete)
while read username action; do
    ${a[$action]}User ${username}
done < myFile

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.