意外的标记“完成”时的Shell语法错误

时间:2021-08-19 01:03:59

This shell script is supposed to add users to the system. The new users details are in a file. The shell is rejecting this script with the message:

这个shell脚本应该将用户添加到系统中。新的用户详细信息在一个文件中。shell拒绝这个脚本的信息:

syntax error near unexpected token 'done'.

What's wrong?

怎么了?

#!/bin/bash
#Purpose: Automatically add new users in a linux system based upon the data found within a text file
#         Assign encryped passwords to each user
#         Add users to groups or create new groups for these users
#         Report errors and successful operations where necessary in log files
#         post help options (echo)

#Root validation
if [[ $(id -u) -eq 0 ]]; then
  #Argument validation
  if [[ -z "$1" ]]; then
    echo "No arguments found!"
    echo "Please include a user detail text file as first argument"
    echo "Please include a report text file as second argument"
    echo "Please include an error report text file as the third argument"
    echo "Use the -h argument (i.e. ./script -h) for help"
  exit 1
fi

#Help validation and Help file
if [[ "$1" = "-h" ]]; then
  echo "This is the help information file"
  echo "This script is designed to add users to a linux system by reading information from a user detail file (such as userlist.txt)"
  echo "The format of this user detail text file is "username password groupname fullname" seperated using TAB spacing"
  echo "This script will read the first argument as the user detail file, the second and third arguments will be read as a success report file and error report file respectively"
  exit
fi

#Reads first argument as user detail file for data
cat userlist.txt | while read uname password gname fullname
#Reads /etc/passwd for Username
egrep -w "^$uname" /etc/passwd
#If Username is found then error reports
if [ $? == 0 ]; then
  echo "User Already Exists : Error adding user with username $uname;$gname;$fullname" >> Successes1.log
  exit 1
else
  #Reads /etc/group for Groupname
  egrep -w "^$gname" /etc/group
  #If Groupname is found then nothing
  if [ $? == 0 ]; then
    echo ""
  else
    #If Groupname not found then creates new group and reports
    groupadd "$gname"
    echo "Group Not Found: New Group $gname was created" >> Successes1.log
  fi
  #Retrieves Date
  createddate=$(date)
  #Perl password script takes input from Userlist
  pass=$(perl -e 'print crypt($ARGV[0], "Password")' "$password")
  #Adds Users with variables from userlist
  useradd "$uname" -g "$gname"  -c "$fullname" -p "$pass"
  #Reports information to successlist and errorlist report files
  if [ $? == 0 ]; then
    groupid=$(id -g $uname)
    userid=$(id -u $uname)
    echo "User Successfully Added: $uname;$userid;$gname;$groupid;$createddate;$fullname" >> Successes1.log
  else
    groupid=$(id -g $uname)
    userid=$(id -u $uname)
    echo "Useradd Error Occurred: $uname;$userid;$gname;$groupid;$createddate;$fullname" >> Errors1.log
    echo "Error: Must be root user to execute script"
    exit
  fi
done

Second attempt

Using some of the ideas from the answers, I came up with a second attempt:

我从答案中运用了一些想法,提出了第二次尝试:

#!/bin/bash
#Purpose: Automatically add new users in a linux system based upon the data found within a text file
#         Assign encryped passwords to each user
#         Add users to groups or create new groups for these users
#         Report errors and successful operations where necessary in log files
#         post help options (echo)

#Root validation
if [[ $(id -u) -eq 0 ]]; then
  #Argument validation
  if [[ -z "$1" ]]; then
    echo "Usage: $0 usernames report errors" 1>&2
    echo "Please include a user detail text file as first argument"
    echo "Please include a report text file as second argument"
    echo "Please include an error report text file as the third argument"
    echo "Use the -h argument (i.e. ./script -h) for help"
    exit 1
  fi
fi

#Help validation and Help file
if [[ "$1" = "-h" ]]; then
  echo "This is the help information file"
  echo "This script is designed to add users to a linux system by reading information from a user detail file (such as userlist.txt)"
  echo "The format of this user detail text file is "username password groupname fullname" seperated using TAB spacing"
  echo "This script will read the first argument as the user detail file, the second and third arguments will be read as a success report file and error report file respectively"
  exit
fi

#Reads first argument as user detail file for data
cat jan.txt | while read uname password gname fullname; do
#Reads /etc/passwd for Username
 egrep -w "^$uname:" /etc/passwd >/dev/null 2>&1
#If Username is found then error reports
if [ $? == 0 ] 
then
  echo "User Already Exists : Error adding user with username $uname;$gname;$fullname" >> Errors1.log

else
  #Reads /etc/group for Groupname
  egrep -w "^$gname" /etc/group
  #If Groupname is found then nothing
if [ $? == 0 ]; then
    echo ""
else
  #If Groupname not found then creates new group and reports
  groupadd "$gname"
    echo "Group Not Found: New Group $gname was created" >> Successes1.log
done < $1
#Retrieves Date
createddate=$(date)
#Perl password script takes input from Userlist
pass=$(perl -e 'print crypt($ARGV[0], "Password")' "$password")
#Adds Users with variables from userlist
useradd "$uname" -g "$gname"  -c "$fullname" -p "$pass"
#Reports information to successlist and errorlist report files
if [ $? == 0 ]
then
  groupid=$(id -g $uname)
  userid=$(id -u $uname)
  echo "User Successfully Added: $uname;$userid;$gname;$groupid;$createddate;$fullname" >> Successes1.log
else
  groupid=$(id -g $uname)
  userid=$(id -u $uname)
  echo "Useradd Error Occurred: $uname;$userid;$gname;$groupid;$createddate;$fullname" >> Errors1.log
  echo "Error: Must be root user to execute script"
  exit 1
fi
fi
done

This does not seem to work properly either. What's wrong now? Seems to show arguments and runs however no users nor group have been added therefore no logs have been created

这似乎也不正常。现在有什么问题吗?似乎显示参数并运行,但是没有添加用户或组,因此没有创建日志。

3 个解决方案

#1


5  

The if starting at:

如果开始:

if [ $? == 0 ]; then
  echo "User Already Exists : Error adding user with username ...
  exit 1
else

is ended with the done instead of the fi that is required.

以完成而不是必需的fi结束。

The while loop starting a couple of lines earlier:

while循环开始前几行:

cat userlist.txt | while read uname password gname fullname

is missing its do (another bug); if that was present, then it would also need the done at the end. Someone lost track of the indentation. (Using 2 characters per level is better than 0 or 1, but it is easier to track levels if you use 4 spaces per level.) Note that the shell hasn't gotten around to complaining about the lack of do because the syntax for a while loop is:

丢失了它的功能(另一个bug);如果是这样,那么它也需要在最后完成。有人忘记了压痕。(每个级别使用2个字符比0或1更好,但是如果每个级别使用4个空格,那么跟踪级别就更容易了。)注意,shell并没有抱怨缺少do,因为while循环的语法是:

while cmd1
      cmd2
      cmd3 ...
do

and as far as the shell is concerned, it is still processing commands in the list cmd1, cmd2, cmd3, ....

shell而言,它仍然是处理列表中的命令cmd1 cmd2,cmd3,....


Here's a semi-decently indented version of the script. There was a missing fi at the top of the script, too.

这是一个半衰期缩进的脚本。剧本的顶部也有一个缺失的fi。

#!/bin/bash
#Purpose: Automatically add new users in a linux system based upon the data found within a text file
#         Assign encryped passwords to each user
#         Add users to groups or create new groups for these users
#         Report errors and successful operations where necessary in log files
#         post help options (echo)

#Root validation
if [[ $(id -u) -eq 0 ]]
then
    #Argument validation
    if [[ -z "$1" ]]
    then
        echo "No arguments found!"
        echo "Please include a user detail text file as first argument"
        echo "Please include a report text file as second argument"
        echo "Please include an error report text file as the third argument"
        echo "Use the -h argument (i.e. ./script -h) for help"
        exit 1
    fi
fi

#Help validation and Help file
if [[ "$1" = "-h" ]]
then
    echo "This is the help information file"
    echo "This script is designed to add users to a linux system by reading information from a user detail file (such as userlist.txt)"
    echo "The format of this user detail text file is "username password groupname fullname" seperated using TAB spacing"
    echo "This script will read the first argument as the user detail file, the second and third arguments will be read as a success report file and error report file respectively"
    exit
fi

#Reads first argument as user detail file for data
cat userlist.txt | while read uname password gname fullname
do
    #Reads /etc/passwd for Username
    egrep -w "^$uname" /etc/passwd
    #If Username is found then error reports
    if [ $? == 0 ]
    then
        echo "User Already Exists : Error adding user with username $uname;$gname;$fullname" >> Successes1.log
        exit 1
    else
        #Reads /etc/group for Groupname
        egrep -w "^$gname" /etc/group
        #If Groupname is found then nothing
        if [ $? == 0 ]
        then
            echo ""
        else
            #If Groupname not found then creates new group and reports
            groupadd "$gname"
            echo "Group Not Found: New Group $gname was created" >> Successes1.log
        fi
        #Retrieves Date
        createddate=$(date)
        #Perl password script takes input from Userlist
        pass=$(perl -e 'print crypt($ARGV[0], "Password")' $pass)
        #Adds Users with variables from userlist
        useradd "$uname" -g "$gname"  -c "$fullname" -p "$pass"
        #Reports information to successlist and errorlist report files
        if [ $? == 0 ]
        then
            groupid=$(id -g $uname)
            userid=$(id -u $uname)
            echo "User Successfully Added: $uname;$userid;$gname;$groupid;$createddate;$fullname" >> Successes1.log
        else
            groupid=$(id -g $uname)
            userid=$(id -u $uname)
            echo "Useradd Error Occurred: $uname;$userid;$gname;$groupid;$createddate;$fullname" >> Errors1.log
            echo "Error: Must be root user to execute script"
            exit
        fi
    fi
done

There is still much room for improvement. The root validation block should exit if the user is not root; that happens instead a mile further down inside the loop. You can check the number of arguments better: $# gives you the number of arguments. If I tried yourscript.sh '' arg2 arg3, you'd claim there were no arguments when in fact the problem is that $1 is present but is an empty string. The standard convention for reporting how to use a command is something like:

还有很大的改进空间。如果用户不是根,根验证块应该退出;相反,这发生在一英里内的环内。您可以更好地检查参数的数量:$#提供了参数的数量。如果我试着yourscript。“arg2 arg3”,你会说没有参数,实际上问题是1美元存在,但是是一个空字符串。报告如何使用命令的标准惯例是:

echo "Usage: $0 usernames report errors" 1>&2

This reports the command's name, and the arguments expected. The 1>&2 sends the message to standard error instead of standard output. The logic here is a little bizarre even so. You check that the user is root and only then check that there are arguments. If the user is not root, you don't check the arguments. Not entirely sensible, I submit.

这将报告命令的名称和预期的参数。>和2将消息发送到标准错误而不是标准输出。这里的逻辑有点奇怪。您检查用户是根,然后检查是否有参数。如果用户不是root用户,则不检查参数。我认为这并不完全合理。

We can debate the UUOC (Useless Use of Cat). There's actually an award for it; I don't think this qualifies. However, it would be possible to write:

我们可以讨论UUOC(猫的无用使用)。实际上有一个奖项;我不认为这是有资格的。但是,可以这样写:

while read uname password gname fullname
do
    ...
done < $1

Hmmm...the script is supposed to take a file name argument that specifies the users, but the cat takes a fixed file name, not the file name argument!

嗯…脚本应该使用指定用户的文件名参数,但是cat使用的是固定文件名,而不是文件名参数!

Similarly, arguments 2 and 3 are studiously ignored; the log files are hard-coded.

类似地,第2和第3条被故意忽略;日志文件是硬编码的。

egrep -w "^$uname" /etc/passwd
#If Username is found then error reports
if [ $? == 0 ]

This fragment can be improved several ways:

这个片段可以通过多种方式改进:

if egrep -w "^$uname:" /etc/passwd >/dev/null 2>&1
then
    #If Username is found then error report

This tests the exit status of the egrep command directly; it also prevents a new user roo from being treated as pre-existing because of user root. It sends the output and error output to /dev/null so that you won't see anything when the user does exist.

这直接测试了白鹭命令的退出状态;它还防止新用户roo因用户root而被视为已存在的用户。它将输出和错误输出发送到/dev/null,以便在用户存在时看不到任何内容。

It might be better not to exit when the user name is found; you could at least try to process the next entry. It is also odd that the report that the user exists (which terminates the processing) is recorded in Successes1.log rather than in Errors1.log; it is treated like an error.

当发现用户名时,最好不要退出;您至少可以尝试处理下一个条目。用户存在(终止处理)的报告在Successes1中记录也很奇怪。log而不是Errors1.log;它被视为一个错误。

The group check constructs are similar and should be similarly upgraded.

组检查结构相似,应该进行类似的升级。

You read the password into $password with the read line; when it comes to creating the password, though, you have:

你用读行读取密码到$password;但是,在创建密码时,你有:

pass=$(perl -e 'print crypt($ARGV[0], "Password")' $pass)

On the first cycle, $pass is empty (most probably); you should have used $password in double quotes at the end:

在第一个循环中,$pass是空的(很可能是);您应该在最后使用$password双引号:

pass=$(perl -e 'print crypt($ARGV[0], "Password")' "$password")

As with the egrep commands, you can check the status of the useradd command directly too. It is a bit sweeping to say if [ $? == 0 ] is the mark of a tyro, but it isn't too far off the truth.

与白鹭命令一样,您也可以直接检查useradd命令的状态。如果[$?是一个初学者的标志,但也不是太离谱。

The final exit in the script should be exit 1 to indicate an error exit. As already noted, this is preceded by the comment about 'you must be root', even though there was a check at the top for root privileges.

脚本的最后一个出口应该是exit 1,以指示错误退出。如前所述,在前面有关于“您必须是根”的注释,尽管在顶部有一个检查根特权的检查。

Caveat: I've not attempted to run the script; I could easily have missed some issues. It does, however, pass sh -v -n, so there are no gross syntactic errors left.

注意:我没有尝试运行脚本;我很容易漏掉一些问题。然而,它确实通过了sh -v -n,因此没有出现总的语法错误。

Once a shell script is syntactically correct, then you normally debug it using sh -x script (or, if it takes arguments, then sh -x script arg1 arg2 arg3 ...). This is the execution trace mode. The shell tells you, more or less inscrutably, what it is doing. The information is written to standard error. You can even trap the output for later scrutiny if you like with:

一旦shell脚本在语法上是正确的,那么您通常使用sh -x脚本调试它(或者,如果它接受参数,那么sh -x脚本arg1 arg2 arg3…)。这是执行跟踪模式。外壳或多或少地告诉你它在做什么。信息被写入标准错误。如果你喜欢的话,你甚至可以将输出设置为以后的详细检查:

sh -x script arg1 arg2 arg3 2>script-x.log

The 2>script-x.log notation sends the standard error to the file script-x.log (choose your own meaningful name; I often use x or xxx for files I won't want to keep, but I also remove such files without necessarily even looking at them because I know they are throwaway files).

2 >脚本x。日志表示法将标准错误发送到文件脚本-x。日志(选择自己有意义的名称;我经常使用x或xxx来做我不想保留的文件,但我也会删除这些文件,甚至不看它们,因为我知道它们是一次性文件。

#2


0  

"done" is supposed to be paired with a previous "do" It does not man th end of your script.

“done”应该与之前的“do”配对,而不是你的脚本的末尾。

#3


0  

Your while is missing a do:

你错过了一个机会:

cat userlist.txt | while read uname password gname fullname
do 

or

cat userlist.txt | while read uname password gname fullname; do

#1


5  

The if starting at:

如果开始:

if [ $? == 0 ]; then
  echo "User Already Exists : Error adding user with username ...
  exit 1
else

is ended with the done instead of the fi that is required.

以完成而不是必需的fi结束。

The while loop starting a couple of lines earlier:

while循环开始前几行:

cat userlist.txt | while read uname password gname fullname

is missing its do (another bug); if that was present, then it would also need the done at the end. Someone lost track of the indentation. (Using 2 characters per level is better than 0 or 1, but it is easier to track levels if you use 4 spaces per level.) Note that the shell hasn't gotten around to complaining about the lack of do because the syntax for a while loop is:

丢失了它的功能(另一个bug);如果是这样,那么它也需要在最后完成。有人忘记了压痕。(每个级别使用2个字符比0或1更好,但是如果每个级别使用4个空格,那么跟踪级别就更容易了。)注意,shell并没有抱怨缺少do,因为while循环的语法是:

while cmd1
      cmd2
      cmd3 ...
do

and as far as the shell is concerned, it is still processing commands in the list cmd1, cmd2, cmd3, ....

shell而言,它仍然是处理列表中的命令cmd1 cmd2,cmd3,....


Here's a semi-decently indented version of the script. There was a missing fi at the top of the script, too.

这是一个半衰期缩进的脚本。剧本的顶部也有一个缺失的fi。

#!/bin/bash
#Purpose: Automatically add new users in a linux system based upon the data found within a text file
#         Assign encryped passwords to each user
#         Add users to groups or create new groups for these users
#         Report errors and successful operations where necessary in log files
#         post help options (echo)

#Root validation
if [[ $(id -u) -eq 0 ]]
then
    #Argument validation
    if [[ -z "$1" ]]
    then
        echo "No arguments found!"
        echo "Please include a user detail text file as first argument"
        echo "Please include a report text file as second argument"
        echo "Please include an error report text file as the third argument"
        echo "Use the -h argument (i.e. ./script -h) for help"
        exit 1
    fi
fi

#Help validation and Help file
if [[ "$1" = "-h" ]]
then
    echo "This is the help information file"
    echo "This script is designed to add users to a linux system by reading information from a user detail file (such as userlist.txt)"
    echo "The format of this user detail text file is "username password groupname fullname" seperated using TAB spacing"
    echo "This script will read the first argument as the user detail file, the second and third arguments will be read as a success report file and error report file respectively"
    exit
fi

#Reads first argument as user detail file for data
cat userlist.txt | while read uname password gname fullname
do
    #Reads /etc/passwd for Username
    egrep -w "^$uname" /etc/passwd
    #If Username is found then error reports
    if [ $? == 0 ]
    then
        echo "User Already Exists : Error adding user with username $uname;$gname;$fullname" >> Successes1.log
        exit 1
    else
        #Reads /etc/group for Groupname
        egrep -w "^$gname" /etc/group
        #If Groupname is found then nothing
        if [ $? == 0 ]
        then
            echo ""
        else
            #If Groupname not found then creates new group and reports
            groupadd "$gname"
            echo "Group Not Found: New Group $gname was created" >> Successes1.log
        fi
        #Retrieves Date
        createddate=$(date)
        #Perl password script takes input from Userlist
        pass=$(perl -e 'print crypt($ARGV[0], "Password")' $pass)
        #Adds Users with variables from userlist
        useradd "$uname" -g "$gname"  -c "$fullname" -p "$pass"
        #Reports information to successlist and errorlist report files
        if [ $? == 0 ]
        then
            groupid=$(id -g $uname)
            userid=$(id -u $uname)
            echo "User Successfully Added: $uname;$userid;$gname;$groupid;$createddate;$fullname" >> Successes1.log
        else
            groupid=$(id -g $uname)
            userid=$(id -u $uname)
            echo "Useradd Error Occurred: $uname;$userid;$gname;$groupid;$createddate;$fullname" >> Errors1.log
            echo "Error: Must be root user to execute script"
            exit
        fi
    fi
done

There is still much room for improvement. The root validation block should exit if the user is not root; that happens instead a mile further down inside the loop. You can check the number of arguments better: $# gives you the number of arguments. If I tried yourscript.sh '' arg2 arg3, you'd claim there were no arguments when in fact the problem is that $1 is present but is an empty string. The standard convention for reporting how to use a command is something like:

还有很大的改进空间。如果用户不是根,根验证块应该退出;相反,这发生在一英里内的环内。您可以更好地检查参数的数量:$#提供了参数的数量。如果我试着yourscript。“arg2 arg3”,你会说没有参数,实际上问题是1美元存在,但是是一个空字符串。报告如何使用命令的标准惯例是:

echo "Usage: $0 usernames report errors" 1>&2

This reports the command's name, and the arguments expected. The 1>&2 sends the message to standard error instead of standard output. The logic here is a little bizarre even so. You check that the user is root and only then check that there are arguments. If the user is not root, you don't check the arguments. Not entirely sensible, I submit.

这将报告命令的名称和预期的参数。>和2将消息发送到标准错误而不是标准输出。这里的逻辑有点奇怪。您检查用户是根,然后检查是否有参数。如果用户不是root用户,则不检查参数。我认为这并不完全合理。

We can debate the UUOC (Useless Use of Cat). There's actually an award for it; I don't think this qualifies. However, it would be possible to write:

我们可以讨论UUOC(猫的无用使用)。实际上有一个奖项;我不认为这是有资格的。但是,可以这样写:

while read uname password gname fullname
do
    ...
done < $1

Hmmm...the script is supposed to take a file name argument that specifies the users, but the cat takes a fixed file name, not the file name argument!

嗯…脚本应该使用指定用户的文件名参数,但是cat使用的是固定文件名,而不是文件名参数!

Similarly, arguments 2 and 3 are studiously ignored; the log files are hard-coded.

类似地,第2和第3条被故意忽略;日志文件是硬编码的。

egrep -w "^$uname" /etc/passwd
#If Username is found then error reports
if [ $? == 0 ]

This fragment can be improved several ways:

这个片段可以通过多种方式改进:

if egrep -w "^$uname:" /etc/passwd >/dev/null 2>&1
then
    #If Username is found then error report

This tests the exit status of the egrep command directly; it also prevents a new user roo from being treated as pre-existing because of user root. It sends the output and error output to /dev/null so that you won't see anything when the user does exist.

这直接测试了白鹭命令的退出状态;它还防止新用户roo因用户root而被视为已存在的用户。它将输出和错误输出发送到/dev/null,以便在用户存在时看不到任何内容。

It might be better not to exit when the user name is found; you could at least try to process the next entry. It is also odd that the report that the user exists (which terminates the processing) is recorded in Successes1.log rather than in Errors1.log; it is treated like an error.

当发现用户名时,最好不要退出;您至少可以尝试处理下一个条目。用户存在(终止处理)的报告在Successes1中记录也很奇怪。log而不是Errors1.log;它被视为一个错误。

The group check constructs are similar and should be similarly upgraded.

组检查结构相似,应该进行类似的升级。

You read the password into $password with the read line; when it comes to creating the password, though, you have:

你用读行读取密码到$password;但是,在创建密码时,你有:

pass=$(perl -e 'print crypt($ARGV[0], "Password")' $pass)

On the first cycle, $pass is empty (most probably); you should have used $password in double quotes at the end:

在第一个循环中,$pass是空的(很可能是);您应该在最后使用$password双引号:

pass=$(perl -e 'print crypt($ARGV[0], "Password")' "$password")

As with the egrep commands, you can check the status of the useradd command directly too. It is a bit sweeping to say if [ $? == 0 ] is the mark of a tyro, but it isn't too far off the truth.

与白鹭命令一样,您也可以直接检查useradd命令的状态。如果[$?是一个初学者的标志,但也不是太离谱。

The final exit in the script should be exit 1 to indicate an error exit. As already noted, this is preceded by the comment about 'you must be root', even though there was a check at the top for root privileges.

脚本的最后一个出口应该是exit 1,以指示错误退出。如前所述,在前面有关于“您必须是根”的注释,尽管在顶部有一个检查根特权的检查。

Caveat: I've not attempted to run the script; I could easily have missed some issues. It does, however, pass sh -v -n, so there are no gross syntactic errors left.

注意:我没有尝试运行脚本;我很容易漏掉一些问题。然而,它确实通过了sh -v -n,因此没有出现总的语法错误。

Once a shell script is syntactically correct, then you normally debug it using sh -x script (or, if it takes arguments, then sh -x script arg1 arg2 arg3 ...). This is the execution trace mode. The shell tells you, more or less inscrutably, what it is doing. The information is written to standard error. You can even trap the output for later scrutiny if you like with:

一旦shell脚本在语法上是正确的,那么您通常使用sh -x脚本调试它(或者,如果它接受参数,那么sh -x脚本arg1 arg2 arg3…)。这是执行跟踪模式。外壳或多或少地告诉你它在做什么。信息被写入标准错误。如果你喜欢的话,你甚至可以将输出设置为以后的详细检查:

sh -x script arg1 arg2 arg3 2>script-x.log

The 2>script-x.log notation sends the standard error to the file script-x.log (choose your own meaningful name; I often use x or xxx for files I won't want to keep, but I also remove such files without necessarily even looking at them because I know they are throwaway files).

2 >脚本x。日志表示法将标准错误发送到文件脚本-x。日志(选择自己有意义的名称;我经常使用x或xxx来做我不想保留的文件,但我也会删除这些文件,甚至不看它们,因为我知道它们是一次性文件。

#2


0  

"done" is supposed to be paired with a previous "do" It does not man th end of your script.

“done”应该与之前的“do”配对,而不是你的脚本的末尾。

#3


0  

Your while is missing a do:

你错过了一个机会:

cat userlist.txt | while read uname password gname fullname
do 

or

cat userlist.txt | while read uname password gname fullname; do