3
\$\begingroup\$

I want to read 4 columns, namely UID, GID, username and home directory of specific users from /etc/passwd into an SQLite database. I found this to create many subprocesses, and I'd like to have some improvement suggestions.

#!/bin/sh
db="$PWD/users.db"
users=$(grep -E 'joe|bill|tom' /etc/passwd)
sqlite3 "$db" <<EOF
DROP TABLE Users;
CREATE TABLE Users \
    (UID INTEGER PRIMARY KEY, GID INTEGER, Username TEXT, Home TEXT);
EOF
echo "$users" | while IFS= read -r line
do
    sqlite3 "$db" \
        "INSERT INTO Users VALUES ( \
         $(echo "$line" | cut -d":" -f3),   \
         $(echo "$line" | cut -d":" -f4),   \
        '$(echo "$line" | cut -d":" -f1)',  \
        '$(echo "$line" | cut -d":" -f6)');"
done
\$\endgroup\$
0

1 Answer 1

1
\$\begingroup\$

Just don't create that many subprocesses. Your objective is to prepare a string of INSERT statements and feed it to one invocation of sqlite3. So drop the whole while read block and do the required text manipulations on the queried passwd entries before passing them off to sqlite3:

#!/bin/sh
db='./users.db'
sqlite3 "$db" <<EOF
DROP TABLE Users;
CREATE TABLE Users \
    (UID INTEGER PRIMARY KEY, GID INTEGER, Username TEXT, Home TEXT);
$(getent passwd 'joe' 'bill' 'tom' | awk -F':' -v quote="'" -v OFS="', '" \
    '{print "INSERT INTO Users VALUES ("quote $3, $4, $1, $6 quote");"}')
EOF

Doing a grep on /etc/passwd has a few drawbacks: the passwd file might not be located in /etc (I don't know of any Unix-like OS for which this is true, though), and it won't have the users that are stored in LDAP/NIS, etc. On the other hand, some systems don't have getent, so this method is not perfect, either. You might want to fall back to grep if getent returns a "command not found".

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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