21

I was wondering if there's a way to find out the default shell of the current user within a shell script?

Use case: I am working on a script that sets an alias for a command and this alias is set within a shell script.

!# /bin/bash
alias = 'some command to set the alias'

There's a logic in the script where it tries to find the default shell of the user that executes the script and adds this alias in the respective ~/.bashrc or ~/.zshrc file

But as I am adding a shebang in the front of the script and explicitly asking it to use bash, answers posted here always return bash as expected although I am executing this script on a ZSH terminal.

Is there a way to get the shell type where the script is executed regardless of the shebang set?

I am looking for a solution that works on both Mac and all the linux based bistro.

  • @StephenRauch I should have mentioned it earlier, I am looking for a method that works on both Mac and Linux based system. /etc/passwd on Mac does not contain the user information and its only consulted in a single user mode. – Spaniard89 Mar 18 '17 at 15:46
  • What does /etc/passwd have on OSX? I had a quick look online and it appears that the shell is there, just not the username. The user ID's still there though, why don't you use that? – terdon Mar 18 '17 at 16:03
  • I'd wonder if it's even possible to have an exhaustive list of shells. Or do all shell startup files always conform to .{name_of_shell}rc? – jamesqf Mar 18 '17 at 18:19
  • Here is an answer to OSX https://stackoverflow.com/a/16375660/1049542 – Sergey Ponomarev Nov 20 '20 at 07:06

10 Answers10

27

The environment variable, SHELL would always expands to the default login shell of the invoking user (gets the value from /etc/passwd).

For any other given user, you need to do some processing with /etc/passwd, here is a simple awk snippet:

awk -F: -v user="foobar" '$1 == user {print $NF}' /etc/passwd

Replace foobar with the actual username.

If you have ldap (or something similar in place), use getent to get the database instead of directly parsing /etc/passwd:

getent passwd | awk -F: -v user="foobar" '$1 == user {print $NF}'

or cleanest approach, let getent do the parsing (thanks to @Kusalananda):

getent passwd foobar | awk -F: '{print $NF}'
heemayl
  • 56,300
  • 7
    getent passwd foobar to skip having to match a particular line. – Kusalananda Mar 18 '17 at 15:45
  • Thanks for the answer. I should have mentioned it earlier, I am looking for a method that works on both Mac and Linux based system. /etc/passwd on Mac does not contain the user information and its only consulted in a single user mode. – Spaniard89 Mar 18 '17 at 15:46
  • For an explanation of that awk command, see here: https://unix.stackexchange.com/a/589221/114401 – Gabriel Staples May 27 '20 at 07:54
  • Use getent unless you're never ever going to use a directory for authentication ever. If Unix doesn't have getent you need to restate your question to find the equivalent in your particular Unix distro. – Rich Aug 05 '20 at 19:15
  • Alas the SHELL environment variable doesn’t always exist! In fact, even when the parameter exists in a shell, it might not be exported. And using getent (or finger) works well on Linux but fails on some other systems. I’m not aware of a foolproof, maximally portable way. As a fallback I’d just iterate a few common shell path locations and, if those don’t exist, call it a day. – Konrad Rudolph Feb 02 '22 at 18:06
11
$ finger $USER|grep -oP 'Shell: \K.*'
/bin/mksh
Ipor Sircer
  • 14,546
  • 1
  • 27
  • 39
  • Thanks it works like a charm! This is the only solution that probably works for both mac and linux based bistro. – Spaniard89 Mar 18 '17 at 15:49
  • 5
    BTW, I edited the command a little bit to make it agnostic to all OS finger $USER | grep 'Shell:*' | cut -f3 -d ":" as the grep options -P is not supported on Mac. – Spaniard89 Mar 18 '17 at 15:53
  • 6
    Note that finger may not be installed on all machines. It's an optional package on RedHat based systems. – Stephen Harris Mar 18 '17 at 15:56
  • 3
    @Kishorepandey: you can use getent|grep ^$USER:|cut -f: -f7 instead of finger, if finger was not installed by default. – Ipor Sircer Mar 18 '17 at 17:18
  • 1
    @Kishorepandey Don't do this. Use the SHELL environment variable if it's present. Looking at the user database will give you a result (the user's login shell) but it might not be the right result (the user's favorite interactive shell) — it depends on the user's configuration. – Gilles 'SO- stop being evil' Mar 18 '17 at 23:43
  • finger $USER | pcregrep --color -io1 'shell:\s*(.+)' – smac89 Aug 29 '18 at 00:58
  • Responding to Gilles's comment: $SHELL is the user's login shell. My login shell is fish: if I unset the SHELL variable and then launch bash, SHELL is "/path/to/fish" – glenn jackman Jun 22 '22 at 22:37
8

Since getent isn't a standard command on MacOS, you may wish to use a lower level getpwuid call that will consult the naming services the machine is configured for. This may require calling out to perl or python, which are pretty common across most Unix-like platforms

eg this will return the shell for the current user:

perl -e '@x=getpwuid($<); print $x[8]'
4

Maybe this:

grep ^$USER: /etc/passwd | cut -f 7 -d :
Stephen Kitt
  • 434,908
user226946
  • 49
  • 1
3

You can get the user's default shell from the environment like this:

echo $SHELL

But not like this:

echo $0

The latter will just tell you what shell your script is currently using. So you want the first option above.

Alxs
  • 2,200
  • 3
  • 21
  • 31
  • 3
    $SHELL is not guaranteed to be the user's default shell either. You want getent or its macOS equivalent. – Rich Aug 05 '20 at 19:16
2

Short and painless:

getent passwd <USER> | cut -d : -f 7
2

One more way to solve the above problem of finding your default shell, just for fun:

grep ^$USER: /etc/passwd | grep -Eo ':([^:]*)$' | tr -d ':'

Brief description: find the line with the user, find the :/my/shell path at the end, delete the : char from it.


Now, let me expand on @heemayl's answer, and explain the magical awk code he uses.

1. Just remember the simple stuff:

First off, this:

awk -F: -v user="foobar" '$1 == user {print $NF}' /etc/passwd

while being 100% correct, and the most "right" answer perhaps, is impossible to remember--you'd have to learn awk and then rewrite it logically from scratch each time. What is easy to remember, however, is this: the default shell for a user is the last field on the user's line in the "/etc/passwd" file, where fields are separated by the colon : symbol, and each line in that file begins with a username.

Now, with that knowledge, just think: "ok, I need to find my username line in /etc/passwd". So, do this:

grep $USER /etc/passwd

OR (same effect)

cat /etc/passwd | grep $USER

You'll get something like this:

my_username:x:1001:1001::/home/my_username:/bin/bash

Cool! My default shell is therefore the last field, which means whatever is after the last : symbol. It is /bin/bash. Done.

2. Explanation of this awk command:

awk -F: -v user="foobar" '$1 == user {print $NF}' /etc/passwd

awk is a pattern matching language. You can read the GNU awk, or gawk, manual here, which is a great reference: https://www.gnu.org/software/gawk/manual/gawk.html

  1. /etc/passwd at the end is the file to open and process with awk
  2. -F: says to change the "'F'ield separator" from space (the default) to :.
  3. The -v option sets an awk 'v'ariable you are naming user here, to "foobar"
  4. Now the actual awk program is stored between the single quotes. It's simply $1 == user {print $NF}. Remember: it is its own programming language! It is based on a pattern match or boolean check { action } type flow. It scans down the file one "record" (or line, by default) at a time. $1 is the first field in any given line, and remember, we told it that fields are separated by :, so the first field is my_username. So, $1 == user says "does the first field equal the value stored in the awk variable user"? If so, do the action specified in curly braces { }. The action is to print the value of $NF, where the internal NF variable contains the 'N'umber of 'F'ields for this line. So, the number of fields is 7, since :: contains 1 empty field between those two colons. Therefore, $NF is actually $7, or the value of the 7th field, which is /bin/bash for this user. Behold, the magic of the pattern-matching language, awk.
  5. Done.

3. How to configure a default shell for your user:

What if you want to set or change the default shell for a given user? Use chsh. man chsh says it is used to "change login shell". Here's how to use it:

sudo chsh my_username --shell /path/to/my/shell

Ex: set to use the bash shell (my preferred choice) by default:

sudo chsh my_username --shell /bin/bash

Additional awk learning resources:

  1. See some hello world and syntax test files for awk I wrote here: https://github.com/ElectricRCAircraftGuy/eRCaGuy_hello_world/tree/master/awk
  2. Study git diffn, as an example, here. Note: git diffn is a wrapper I wrote around git diff, to show line numbers.
  • In general you should not inspect /etc/passwd manually, it isn’t guaranteed to be synchronised with the user database if directory services (e.g. LDAP) are used. Whereas getent will continue to work (if it is installed). – Konrad Rudolph Feb 02 '22 at 19:04
0

On macOS:

dscl . -read  /Users/$USER UserShell|cut -d' ' -f2
Mojo66
  • 101
-1

Short sed solution:

grep $USER /etc/passwd | sed -r "s/.*\/home\/$USER:(.*)/\1/"
-1

Why is everyone making it so complicated? Just cut it from the /etc/passwd file.

grep "^$USER:" /etc/passwd | cut -d: -f7
  • 2
    That only works on systems where the user db is stored in /etc/passwd, as opposed to LDAP, NIS+, SQL... That also could give you the wrong result if $USER contains regex operators (such as . which is common in usernames). There's no guarantee $USER will contain the current user name. $LOGNAME (from processes that are part of a login session) is standard, so are $(logname), $(id -un) (though the latter only gives one user with the current process uid as uid, which may not be the same user as the one who logged in in cases where several users share the same uid) – Stéphane Chazelas Mar 31 '22 at 16:11