1

I am a student and struggle mightily with creating my own scripts. Generally, I can do a bang up job script-kiddie-ing and adding on to what someone else has done following the provided information and rules (yes I know shame on me) but after quite a few years trying to work with bash/python I just genuinely don't have the needed brain power for this apparently and cannot create from scratch to save my life so here I am. There was a thread on a similar topic from four years ago that seems to be along the right path for what I am trying to accomplish, however, for the life of me I cannot seem to figure out where I am going wrong at. Below is the script I would like to give credit to jesse_b for providing that i've been tinkering with along with the parameters I am needing to use but unfortunately, when I run the script I constantly get what I believe to be man pages for something I think is in the script (possibly useradd?). If someone is able to take me under their wing and provide constructive feedback I would greatly appreciate it.

#! /usr/bin/bash

MY_INPUT='Newbies.csv' declare -a A_SURNAME declare -a A_FIRST declare -a A_USERNAME declare -a A_GROUP declare -a A_SECGROUP declare -a A_COMMENTS while IFS=, read -r COL1 COL2 COL3 COL4 COL5 COL6 TRASH; do A_SURNAME+=("$COL1") A_FIRST+=("$COL2") A_USERNAME+=("$COL3") A_GROUP+=("$COL4") A_SECGROUP+=("$COL5") A_COMMENTS+=("$COL6") done <"$MY_INPUT"

for index in "${!A_USERNAME[@]}"; do useradd -g "${A_GROUP[$index]}" -G "${A_SECGROUP[$index]}" -c "${A_COMMENTS[$index]}"

done

Parameters exampleenter image description here: surname,first,username,group,secgroup,comments:

Esche,Adlai,aesche,Accounting,Emp,5326,
West,Kaila,kwest,Accounting,Emp,0699,
July,Markos,mjuly,MArketing,Emp,7208,

Link to referenced post: Creating Users from a .csv file

steeldriver
  • 81,074
tyvm
  • 11
  • 3
    You are getting the useradd command's own usage message, because the command you gave lacks a mandatory argument. In particular, you have specified no username for the new user (the "${A_USERNAME[$index]}" argument in the answer you linked). – steeldriver Feb 20 '22 at 00:31
  • Hmm, so I thought that by declaring a value in this case the third value in the parameters from the .csv file I am using is the username i.e. aesche/kwest/mjuly that nothing else would be needed here. How would I be able to correct this? – tyvm Feb 20 '22 at 00:40
  • The useradd command has no visibility of the variables declared in the script - it only sees the arguments that you pass to it. – steeldriver Feb 20 '22 at 01:37
  • Apologies on my part, the username is built into the Newbies.csv document. So wouldn't this script be able to pull it from the doc with the variables declared? If no, how should I try to rewrite this script to allow for that? Again, apologies for my lack of knowledge on this topic it is really an area I struggle with. – tyvm Feb 20 '22 at 02:51
  • I've also tried running this through shellcheck and it seems to think that no issues are present so now I am really confused hah... – tyvm Feb 20 '22 at 03:00
  • 1
    Your script is reading it in from the CSV file and adding it to the A_USERNAME array, but it isn't passing it to adduser. You need to add a space and "${A_USERNAME[$index]}" to the end of the adduser line. – cas Feb 20 '22 at 03:46

1 Answers1

0

As well as forgetting to add the username to the end of the useradd line, you're also making this far more complicated than it needs to be.

You don't need multiple array variables (unless you intend to do more with the CSV data after the accounts have been created - and even then, you're probably better off doing that inside the existing for loop). You're also using bash (which is a language that's terrible at text processing) instead of a language that's actually good at text processing (such as awk or perl).

Here's how to do what you want in a perl one-liner:

$ perl -lne '($surname,$first,$username,$group,$secgroup,$comments) = split /\s*,\s*/;
    system("echo","useradd","-g",$group,"-G",$secgroup,"-c",$comments,$username)' Newbies.csv

Example output:

useradd -g Accounting -G Emp -c 5326 aesche
useradd -g Accounting -G Emp -c 0699 kwest
useradd -g MArketing -G Emp -c 7208 mjuly

This is written to be a dry-run, only printing what it would do, without actually adding any user accounts. Remove the "echo" from the system() command once you've verified that it's generating the useradd commands correctly.

Note that, unlike bash, quoting of variables isn't necessary when using perl's system() function - each element of the list given to it is passed as an individual, separate argument to the program being executed by system (in this example, the program is echo, or useradd after you've deleted the "echo" from the list), even if it contains whitespace characters. Unless you pass it wildcards or other shell meta-characters system() does not use the shell and is not subject to your shell's word-splitting or expansion. And it's easy enough to avoid passing wildcards, as perl has its own built-in methods for expanding a glob or constructing an array full of filenames.

The only time you might need to quote variables is if you're combining them into one string. For example, you can add the first name and surname to the GECOS field with:

$ perl -lne '($surname,$first,$username,$group,$secgroup,$comments) = split /\s*,\s*/;
    system("echo","useradd","-g",$group,"-G",$secgroup,"-c","$first $surname - $comments",$username)' Newbies.csv 

and even then, there are many other ways of doing that - e.g. with the . concatenation operator:

$first . " " . $surname . " - " . $comments

or using join():

join(" ",$first,$surname,"-",$comments)

Example output:

useradd -g Accounting -G Emp -c Adlai Esche - 5326 aesche
useradd -g Accounting -G Emp -c Kaila West - 0699 kwest
useradd -g MArketing -G Emp -c Markos July - 7208 mjuly

It's not obvious from the output shown here, but the comments are actually passed as a single argument to useradd's -c option. e.g. Adlai Esche - 5326 is a single argument to the -c option, not four separate arguments, a single string that happens to contain space characters.

You can see this if you replace the "echo", with "printf","%s\n", so that it prints each arg on a separate line - e.g.

$ perl -lne '($surname,$first,$username,$group,$secgroup,$comments) = split /\s*,\s*/;
    system("printf","%s\n","useradd","-g",$group,"-G",$secgroup,"-c","$first $surname - $comments",$username)' Newbies.csv
useradd
-g
Accounting
-G
Emp
-c
Adlai Esche 5326
aesche
...

An awk version would be very similar. The main differences being that awk automatically splits its input into columns ($1, $2, $3....) based on the Field Separator (FS variable, defaults to whitespace but you can set to a comma with -F,) and awk's system function only takes a single string argument so you'd have to do the quoting yourself, similar to what's required for sh or bash.

perl has a similar -F option and can also auto-split its input into columns (into an array called @F), but it's easier to read code with meaningful variable names like $group rather than $F[2].


bash version:

#!/bin/bash

MY_INPUT='Newbies.csv'

while IFS=, read -r surname first username group secgroup comments; do echo useradd -g "$group" -G "$secgroup" -c "$comments" "$username" #echo useradd -g "$group" -G "$secgroup" -c "$first $surname - $comments" "$username" #printf "%s\n" useradd -g "$group" -G "$secgroup" -c "$first $surname - $comments" "$username" done < "$MY_INPUT"

Again, this is written as a dry-run. Remove the echo to allow it to actually create the user accounts. It also has a comment showing how to add the first name and surname to the GECOS field.

Output is the same as in the perl version:

useradd -g Accounting -G Emp -c Adlai Esche - 5326 aesche
useradd -g Accounting -G Emp -c Kaila West - 0699 kwest
useradd -g MArketing -G Emp -c Markos July - 7208 mjuly

Or:

useradd -g Accounting -G Emp -c Adlai Esche - 5326 aesche
useradd -g Accounting -G Emp -c Kaila West - 0699 kwest
useradd -g MArketing -G Emp -c Markos July - 7208 mjuly

Combining bash and perl:

#!/bin/bash

MY_INPUT='Newbies.csv'

perl -lne '($surname,$first,$username,$group,$secgroup,$comments) = split /\s,\s/; system("echo","useradd","-g",$group,"-G",$secgroup,"-c",$comments,$username)' "$MY_INPUT"

This is such a simple task that it doesn't really provide a good example of why it's better to use awk or perl or almost-anything-except-bash for text processing. See Why is using a shell loop to process text considered bad practice?

However, even here the perl version is faster (not really important unless you have thousands of accounts to create) and the split /\s*,\s*/ strips any leading and trailing whitespace from the comma-delimited fields - trivial in perl, significantly more work to do in bash. i.e. it "cleans up" the input data so that it is correct for the purpose we want to use it for - user and group names in unix may not start with a space.

cas
  • 78,579
  • note: the system() function examples above execute the standalone binary echo and printf programs (e.g. from the GNU coreutils package) as found in your PATH, not the bash built-in commands....same as it executes the adduser program. – cas Feb 21 '22 at 03:50
  • cas first off, you are a lifesaver! Regarding your response, for this assignment we are restricted to bash specifically and I think that while awk can be included in it perl is an actual code writer (apologies if this is incorrect). If I wanted to add in information to the existing bash script that we have all been working on specifically something like taking the created username, first name, user id, date user was created and outputting to a data file is there an "easy" way to accomplish this? I thought about using a series of commands and piping to a file but that might not work here. – tyvm Feb 22 '22 at 00:11
  • awk and perl are both scripting languages, with roughly similar capabilities and a roughly similar C-like syntax. sed is a scripting language too, although most people only use it for simple regex search & replace operations because its single-character commands make it difficult to read and write complex scripts. A bash script's job is to use other program(s) (awk, sed, perl, and more) to do work - it's good at coordinating the execution of other programs to do work but generally terrible at doing work itself...but I'll add a bash version to my answer. – cas Feb 22 '22 at 00:45