I'm trying to write a simple bash script in which the user inputs their username, then they are greeted, depending on the time of day by their surname. I currently have the following:

echo Please enter your username
read username
name=$(grep $username /etc/passwd | cut -d ':' -f 5)

h='date +%H'

if [ $h -lt 12]; then
  echo Good morning ${name::-3)

etc. etc.

I have managed to cut the 3 commas off the end of the name that are there, but I want to be able to cut the first name off.

For example:

  • The $name is Amber Martin,,,.
  • I've cut down to Amber Martin.
  • I need to cut down further to Martin.
  • And this needs to work with any name.
  • 1
    Did you mean h=$(date +%H) rather than h='date +%H'? – Toby Speight Apr 27 '17 at 14:10
  • 3
    I'm guessing this is just for an exercise or a personal thing, since parsing names and getting everything correct for every edge case is basically impossible. See kalzumeus.com/2010/06/17/… – Muzer Apr 27 '17 at 14:57
  • 3
    You don't. Guessing names is incredibly complicated. For example, here are some people all of whose last name is "Smith": John Smith, J. W. Smith, John William Smith, John-Jack Smith, John William Watson Smith. Basically, there are so many different naming conventions in different places and cultures that doing this 100% correctly is exceedingly hard. – terdon Apr 27 '17 at 16:31
  • Plus, even if you decide that surname == last name, you need to decide what happens if Cher or Madonna sign up. – origimbo Apr 27 '17 at 17:23
  • 1
    ah the old, Juan Pablo Fernández de Calderón García-Iglesias problem – Neil McGuigan Apr 27 '17 at 19:50

Better to use getent passwd than to read /etc/passwd directly. getent also works with LDAP, NIS and such. I think it exists in most Unixes. (My OS X doesn't have it, but it doesn't have my account in /etc/passwd either, so...)

name=$(getent -- passwd "$USER" | cut -d: -f5)

The string processing can be done with the shell's parameter expansion, these are POSIX compatible:

name=${name%%,*}         # remove anything after the first comma
name=${name%,,,}         # or remove just a literal trailing ",,,"
name=${name##* }         # remove from start until the last space
echo "hello $name"

Use ${name#* } to remove until the first space. (Just hope no-one has a two-part last name, with space in between).

The cut could also be replaced with word-splitting or read, by setting IFS to a colon.

  • This will only work if the unwanted part is literally ,,, (i.e. no location or phone information). It would be more robust to cut everything from the first comma: name=${name%%,*}. – Toby Speight Apr 27 '17 at 14:03
  • Is this compatible with every *nix? Or only linux? – Luciano Andress Martini Apr 27 '17 at 14:08
  • @TobySpeight, ah, yes, that's true. (Hopefully no-one's name contains a comma) – ilkkachu Apr 27 '17 at 16:29
  • 2
    @LucianoAndressMartini, the prefix and suffix -removing string operations are from POSIX, I think getent is common. – ilkkachu Apr 27 '17 at 16:34
#And also /bin/sh looks like to be compatible in debian.  
echo "Hmmm... Your username looks like to be $USER"
name="$(getent passwd $USER | cut -d: -f5 | cut -d, -f1)"
echo "Your full name is $name"
surname="$(echo $name | rev | cut -d' ' -f1 | rev)"
echo "Your surname is $surname"
echo "thank your for using only cut and rev to do that..."
echo "But i am sure there is a better way"

Once you have the GECOS (comment) field, you can simply perform another cut to remove the (empty in your case) location and phone number fields, this time with , as the separator:

name=$(getent passwd "$USER" | cut -d: -f5 | cut -d, -f1)
echo "Hello, ${name##* }-san!"

I'll leave it as an exercise to deal with all the different possibilities of what is a "surname"!

  • 1
    Upvoted for the "falsehoods" link. :) – shoover Apr 27 '17 at 16:01

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.