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
1 Answer 1
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".