verbose declare -x from .bashrc - bash

After using Migration Assistant (on OS X) to copy my files form a case sensitive file partition to a case insensitive file partition, my .bashrc has become verbose each time it is run.
#!/bin/bash
#.bashrc file
alias ls='ls -G'
alias sbrc='source ~/.bashrc'
export GNUTERM=x11
export NWCHEM_TOP=~/install/nwchem-6.0-binary
export
PATH = /opt/local/bin:$PATH
...
The output is now
Last login: Mon Apr 30 11:33:33 on ttys005
declare -x Apple_PubSub_Socket_Render="/tmp/launch-oblOxq/Render"
declare -x COMMAND_MODE="unix2003"
declare -x DISPLAY="/tmp/launch-VdU1C8/org.x:0"
declare -x GNUTERM="x11"
...
vencen#dirac:~$
How can I silence bash?

Somehow my .bashrc file received an extra newline character leaving an isolated export
#!/bin/bash
export
PATH=/opt/local/bin:$PATH
#...
The correct file
#!/bin/bash
export PATH=/opt/local/bin:$PATH
#...
does not generate the unwanted output, typing export on the command line does.

Not sure if this is the problem, but I have seen situations where Migration Assistant leaves your home directory not owned by your user account. Instead, your user account is granted all of the usual access via ACLs. You might check that and try fixing it to see if that makes the problem go away.
To check: ls -lde ~
To fix:
sudo chown -R `id -u`:`id -g` ~

Related

Shell Script runs when manually triggered but does nto exicute when Cron runs it

UPDATE After writing out the cron process per Gordon's comment on this post, it seems that Cron cannot find the script. Any guidance on this would be appreciated.
>>/tmp/HRsnapshot.log 2>&1
/bin/sh: 1: /home/user/Scripts/HRsnapshot.sh: not found
/bin/sh: 1: /home/user/Scripts/HRsnapshot.sh: not found
/bin/sh: 1: /home/user/Scripts/HRsnapshot.sh: not found
I need some assistance in understanding why a cronjob is not executing a script that runs as designed when executed manually from the user account.
Currently, the configuration is as such.
Crontab
#Download HR File from Google and push it to HR Server via SFTP
38 11 * * * BASH_ENV=/home/user/Scripts/preload.sh /home/user/Scripts/HRsnapshot.sh
We are loading user env variables in the preload.sh script
#!/bin/bash
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
. /etc/profile
. ~/.bashrc
export GAMCFGDIR="/home/user2/GAMConfig"
The Bash profile has some callouts in it to an application named GAM
#Setting for Gam Config
export GAMCFGDIR="/home/user2/GAMConfig"
export PATH
alias gam="/home/user2/bin/gamadv-xtd3/gam"
The primary script is designed to download a file using GAM (Python app that simplifies API calls to Google Cloud for a Google Workspace env)
#!/bin/bash
cd /home/user/Scripts
/home/user2/bin/gamadv-xtd3/gam user 'hellodolly#conso.org' get drivefile id '1S4tkzS2lIj360-WFhTy3FBm6kZ7hsL4PvO7PdikffiidI' format 'csv' targetfolder /home/user/SFTP/daily_export/ targetname 'FY2022_HR_Form.csv' overwrite
sshpass -p '*jndn4Kz#8Rwrj' sftp GoogleSFTP#10.0.0.0<<EOF
cd /D:/SFTPRoot/Google_Workflows
put /home/user/SFTP/daily_export/FY2022_HR_Form.csv
exit
EOF
Cronlog
Oct 15 13:07:01 JUMPBOX CRON[5431]: (user) CMD (BASH_ENV=/home/user/Scripts/preload.sh /home/user/Scripts/HRsnapshot.sh)
Here is what the user's env looks like
SHELL=/bin/bash
SUDO_GID=1000
SUDO_COMMAND=/usr/bin/su user
SUDO_USER=user2
PWD=/home/user/Scripts
LOGNAME=user
HOME=/home/user
LANG=en_US.UTF-8
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:
LC_TERMINAL=iTerm2
GAMCFGDIR=/home/user2/GAMConfig
LESSCLOSE=/usr/bin/lesspipe %s %s
TERM=xterm-256color
LESSOPEN=| /usr/bin/lesspipe %s
USER=user
LC_TERMINAL_VERSION=3.4.16
SHLVL=1
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
SUDO_UID=1000
MAIL=/var/mail/user
OLDPWD=/home/user2
All user names, IP addresses, Emails Addresses, and Passwords have been obfuscated
Try changing your preload script, export your PATH after sourcing the files.
#!/bin/bash
. /etc/profile
. ~/.bashrc
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
export GAMCFGDIR="/home/user2/GAMConfig"
When sshpass is in /usr/local/bin you don't want the path changed by /etc/profile or .bashrc.
Thanks, everyone for your help.
Strangest thing. There is something invisible in the copied text for the cronjob that is causing the error.
38 11 * * * BASH_ENV=/home/dpmacrina/Scripts/preload.sh /home/dpmacrina/Scripts/HRsnapshot.sh
03 06 * * * BASH_ENV=/home/dpmacrina/Scripts/preload.sh /home/dpmacrina/Scripts/HRSnapshot.sh
These are the two lines that are in the crontab. The top is what was failing, the bottom is what I typed in new. The bottom works.

Env variables not being picked up by script

Creating a script to pass to a few different people and ran into an env problem. The script wouldn't run unless I supplied it with $PATH, $HOME, and $GOPATH at the beginning of the file. Like so:
HOME=/home/Hustlin
PATH=/home/Hustlin/bin:/home/Hustlin/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/local/go/bin:/bin:/home/Hustlin/go/bin
export GOPATH=$HOME/go
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
This is not advantageous when trying to pass the script around and each person has to set these variables themselves. This file would rarely be run by the User and would most often be run via crontab.
I would love to hear a better way of coding this so I'm not asking everyone I send the script to update these variables.
Thank you all in advance!!!
EDIT
The script is being run via crontab with no special permissions.
1,16,31,46 * * * * /home/Hustlin/directory1/super_cool_script.sh
Here is the script I am running:
#!/bin/bash
# TODO Manually put your $PATH and $HOME here.
PATH=/home/Hustlin/bin:/home/Hustlin/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/local/go/bin:/bin:/home/Hustlin/go/bin
HOME=/home/Hustlin
export GOPATH=$HOME/go
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
# Field1
field1="foo"
# Welcome message.
echo Starting the update process...
# Deposit directory.
mkdir -p $HOME/directory1/sub1/data/body
mkdir -p $HOME/directory1/sub2/system
# Run command
program1 command1
# Run longer command.
program1 command2 field1
sleep 3
program1 command3 -o $HOME/directory1/sub1/data $field1
sleep 1
# Unzip and discard unnecessary files.
unzip $HOME/directory1/sub1/data/$field1 -d $HOME/directory1/sub1/data
rm $HOME/directory1/sub1/data/bar.yaml $HOME/dircetory1/sub1/data/char.txt
rm $HOME/directory1/sub1/data/$field1.zip
# Rename
mv $HOME/directory1/sub1/data/body.json $HOME/directory1/sub1/data/body/$(date -d '1 hour ago' +%d-%m-%Y_%H).json
echo Process complete.
I changed most of the program and command names for privacy. What I did post still represents what is being done and how the files are being moved.
The issue is crontab, not the script.
When you run the script on your terminal, you are logged in a session with all environment variables set, so the script can use it.
But when you run it from crontab it an "empty" session, so it does not have any environment variable set, it doesn't even know about your user.
Run the script on crontab like this:.
su --login Hustlin /home/Hustlin/directory1/super_cool_script.sh
Check this documentation.
http://man7.org/linux/man-pages/man1/su.1.html
bash -l -c /path/to/script will make bash execute all .bashrc and .profile files first, so it will have HOME and PATH variables set.

Reading "bash_profile" is doing two things that are contradicting each other

I'm very confused about how my shell is reading bash_profile.
In root, my ~/.bash_profile looks like so
# .bash_profile
# Get the aliases and functions
if [-f ~/.bashrc ]; then
.~/.bashrc
fi
PATH=$PATH:$HOME/bin:$HOME/sbin:$HOME/usr/sbin:$HOME/usr/bin:/usr/sbin
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
export PATH=$PATH
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH
unset USERNAME
There is no ~/.profile file.
In a user called maruhan, my ~/.bash_profile looks like so
# .bash_profile
# Get the aliases and functions
if [-f ~/.bashrc ]; then
.~/.bashrc
fi
PATH=$PATH:$HOME/bin:$HOME/sbin:$HOME/usr/sbin:$HOME/usr/bin:/usr/sbin
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/maruhan/Desktop/issac:/usr/local/lib
ASDF=$ASDF:/home
export PATH=$PATH
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH
export ASDF=$ASDF
unset USERNAME
And my ~/.profile looks like so
LD_LIBRARY_PATH=/home/maruhan/Desktop/issac:/usr/local/lib:$LD_LIBRARY_PATH
ASDF=/home:$ASDF
export ASDF=$ASDF
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH
You can clearly see that ASDF is not defined in root's bash_profile.
However when I call export, I get this in root.
declare -x ASDF=":/home"
but nothing about LD_LIBRARY_PATH.
Strangely in maruhan, running export shows both ASDF and LD_LIBRARY_PATH.
Also, nothing about ASDF or LD_LIBRARY_PATH exist in /etc/environment. I also don't have a /etc/bash_profile file.
Running echo $0 gives me bash for both root and maruhan.
How come LD_LIBRARY_PATH disappeared in root while ASDF is there?
The rules are a bit complicated. According to bash's man page:
INVOCATION
A login shell is one whose first character of argument zero is a -, or one
started with the --login option.
An interactive shell is one started without non-option arguments (unless -s is
specified) and without the -c option whose standard input and error are both
connected to terminals (as determined by isatty(3)), or one started with the -i
option. PS1 is set and $- includes i if bash is interactive, allowing a shell
script or a startup file to test this state.
... ...
When bash is invoked as an interactive login shell, or as a non-interactive
shell with the --login option, it first reads and executes commands from the
file /etc/profile, if that file exists. After reading that file, it looks for
~/.bash_profile, ~/.bash_login, and ~/.profile, in that order, and reads and
executes commands from the first one that exists and is readable. The --noprofile
option may be used when the shell is started to inhibit this behavior.
... ...
When an interactive shell that is not a login shell is started, bash reads and
executes commands from ~/.bashrc, if that file exists. This may be inhibited by
using the --norc option. The --rcfile file option will force bash to read and
execute commands from file instead of ~/.bashrc.
... ...
Note that on some systems bash may be customized so that it would also execute a system wide rc file (e.g. /etc/bash.bashrc) before sourcing ~/.bashrc for an interactive shell that's not a login shell.
Shells started by a login mechanism (usually with a username/password prompt, like console login, telnet, ssh, ...) are usually login shells. For a login shell, $0 is usually -bash.
[local] % ssh user#host <-- The user is trying to login
Password: P#ssw0rd
[remote] % echo $0
-bash <-- This is a login shell
[remote] % bash <-- This is not a login (no username/password)
[remote] % echo $0
bash <-- Not a login shell
[remote] %
To make life easier I would put all rc things in ~/.bashrc and source ~/.bashrc in ~/.bash_profile. For example:
% cat ~/.bash_profile
[[ -f ~/.bashrc ]] && source ~/.bashrc
% cat ~/.bashrc
# return immediately if not in an interactive shell
[[ $- != *i* ]] && return 0
export FOO=bar
PATH=$PATH:/my/path
%

Sending Bash Aliases to detached screen sessions

I'm on a Linux machine using screen, and I'm attempting to write a (fairly portable) function which runs a bash function in a new, detached screen session which automatically closes upon completion. I've had some success, but I noticed the following behavior:
If I include the definition of mail_submit() in my ~/.bashrc file, I can run
mail_submit foo
in the terminal, and also I can access the alias in a new screen session:
screen -S test
mail_submit foo
However, the following command does not work:
screen -d -m -S test sh -c 'mail_submit foo'
presumably because sh -c starts a fresh shell that has no knowledge of my ~/.bashrc profile. So, I can use the following fix:
screen -d -m -S test sh -c 'source ~/.bashrc; mail_submit foo'
which does work.
But if I want to wrap this functionality up into a bash alias (which is my ultimate goal here), this will cause a weird self-referential situation.
Question: What is an easy way to either have sh -c know the location of my ~/.bashrc profile, or use a variant of sourcing the file and creating an alias?
EDIT: I could save the shell script in my home directory, and create an alias which runs
screen -d -m -S test bash -c '~/mail_submit.sh $1'
but I'd still be curious to hear other possible fixes.
A default ~/.bashrc contains this ([[ "$-" != *i* ]] && return) little piece of code on top of it (or somewhere else in the upper part). This line will prevent the ~/.bashrc from beeing sourced if the bash shell doesn't run in interactive mode.
You could:
Remove this line
Create a new file which will only contain the alias you need and source that
Create a little bash script instead of an alias and run that
Do you mean screen -d -m -S test bash -c 'mail_submit foo'?
It looks like you're trying to run the command with the shell (sh), and not the bourne again shell (bash), which is the shell interpreter which actually reads the ~/.bashrc profile.
Edit: The .bashrc file is not being sourced by default because screen does not create the bash process as a login shell, which is when the .bashrc file is read. Creating a .screenrc file with the line defshell -bash will create the bash process as a login shell instead, which will then call the .bashrc file.

How to get $HOME directory when switching to a different user in bash?

I need to execute part of a bash script as a different user, and inside that user's $HOME directory. However, I'm not sure how to determine this variable. Switching to that user and calling $HOME does not provide the correct location:
# running script as root, but switching to a different user...
su - $different_user
echo $HOME
# returns /root/ but should be /home/myuser
Update:
It appears that the issue is with the way that I am trying to switch users in my script:
$different_user=deploy
# create user
useradd -m -s /bin/bash $different_user
echo "Current user: `whoami`"
# Current user: root
echo "Switching user to $different_user"
# Switching user to deploy
su - $different_user
echo "Current user: `whoami`"
# Current user: root
echo "Current user: `id`"
# Current user: uid=0(root) gid=0(root) groups=0(root)
sudo su $different_user
# Current user: root
# Current user: uid=0(root) gid=0(root) groups=0(root)
What is the correct way to switch users and execute commands as a different user in a bash script?
Update: Based on this question's title, people seem to come here just looking for a way to find a different user's home directory, without the need to impersonate that user.
In that case, the simplest solution is to use tilde expansion with the username of interest, combined with eval (which is needed, because the username must be given as an unquoted literal in order for tilde expansion to work):
eval echo "~$different_user" # prints $different_user's home dir.
Note: The usual caveats regarding the use of eval apply; in this case, the assumption is that you control the value of $different_user and know it to be a mere username.
By contrast, the remainder of this answer deals with impersonating a user and performing operations in that user's home directory.
Note:
Administrators by default and other users if authorized via the sudoers file can impersonate other users via sudo.
The following is based on the default configuration of sudo - changing its configuration can make it behave differently - see man sudoers.
The basic form of executing a command as another user is:
sudo -H -u someUser someExe [arg1 ...]
# Example:
sudo -H -u root env # print the root user's environment
Note:
If you neglect to specify -H, the impersonating process (the process invoked in the context of the specified user) will report the original user's home directory in $HOME.
The impersonating process will have the same working directory as the invoking process.
The impersonating process performs no shell expansions on string literals passed as arguments, since no shell is involved in the impersonating process (unless someExe happens to be a shell) - expansions by the invoking shell - prior to passing to the impersonating process - can obviously still occur.
Optionally, you can have an impersonating process run as or via a(n impersonating) shell, by prefixing someExe either with -i or -s - not specifying someExe ... creates an interactive shell:
-i creates a login shell for someUser, which implies the following:
someUser's user-specific shell profile, if defined, is loaded.
$HOME points to someUser's home directory, so there's no need for -H (though you may still specify it)
The working directory for the impersonating shell is the someUser's home directory.
-s creates a non-login shell:
no shell profile is loaded (though initialization files for interactive nonlogin shells are; e.g., ~/.bashrc)
Unless you also specify -H, the impersonating process will report the original user's home directory in $HOME.
The impersonating shell will have the same working directory as the invoking process.
Using a shell means that string arguments passed on the command line MAY be subject to shell expansions - see platform-specific differences below - by the impersonating shell (possibly after initial expansion by the invoking shell); compare the following two commands (which use single quotes to prevent premature expansion by the invoking shell):
# Run root's shell profile, change to root's home dir.
sudo -u root -i eval 'echo $SHELL - $USER - $HOME - $PWD'
# Don't run root's shell profile, use current working dir.
# Note the required -H to define $HOME as root`s home dir.
sudo -u root -H -s eval 'echo $SHELL - $USER - $HOME - $PWD'
What shell is invoked is determined by "the SHELL environment variable if it is set or the shell as specified in passwd(5)" (according to man sudo). Note that with -s it is the invoking user's environment that matters, whereas with -i it is the impersonated user's.
Note that there are platform differences regarding shell-related behavior (with -i or -s):
sudo on Linux apparently only accepts an executable or builtin name as the first argument following -s/-i, whereas OSX allows passing an entire shell command line; e.g., OSX accepts sudo -u root -s 'echo $SHELL - $USER - $HOME - $PWD' directly (no need for eval), whereas Linux doesn't (as of sudo 1.8.95p).
Older versions of sudo on Linux do NOT apply shell expansions to arguments passed to a shell; for instance, with sudo 1.8.3p1 (e.g., Ubuntu 12.04), sudo -u root -H -s echo '$HOME' simply echoes the string literal "$HOME" instead of expanding the variable reference in the context of the root user. As of at least sudo 1.8.9p5 (e.g., Ubuntu 14.04) this has been fixed. Therefore, to ensure expansion on Linux even with older sudo versions, pass the the entire command as a single argument to eval; e.g.: sudo -u root -H -s eval 'echo $HOME'. (Although not necessary on OSX, this will work there, too.)
The root user's $SHELL variable contains /bin/sh on OSX 10.9, whereas it is /bin/bash on Ubuntu 12.04.
Whether the impersonating process involves a shell or not, its environment will have the following variables set, reflecting the invoking user and command: SUDO_COMMAND, SUDO_USER, SUDO_UID=, SUDO_GID.
See man sudo and man sudoers for many more subtleties.
Tip of the hat to #DavidW and #Andrew for inspiration.
In BASH, you can find a user's $HOME directory by prefixing the user's login ID with a tilde character. For example:
$ echo ~bob
This will echo out user bob's $HOME directory.
However, you say you want to be able to execute a script as a particular user. To do that, you need to setup sudo. This command allows you to execute particular commands as either a particular user. For example, to execute foo as user bob:
$ sudo -i -ubob -sfoo
This will start up a new shell, and the -i will simulate a login with the user's default environment and shell (which means the foo command will execute from the bob's$HOME` directory.)
Sudo is a bit complex to setup, and you need to be a superuser just to be able to see the shudders file (usually /etc/sudoers). However, this file usually has several examples you can use.
In this file, you can specify the commands you specify who can run a command, as which user, and whether or not that user has to enter their password before executing that command. This is normally the default (because it proves that this is the user and not someone who came by while the user was getting a Coke.) However, when you run a shell script, you usually want to disable this feature.
For the sake of an alternative answer for those searching for a lightweight way to just find a user's home dir...
Rather than messing with su hacks, or bothering with the overhead of launching another bash shell just to find the $HOME environment variable...
Lightweight Simple Homedir Query via Bash
There is a command specifically for this: getent
getent passwd someuser | cut -f6 -d:
getent can do a lot more... just see the man page. The passwd nsswitch database will return the user's entry in /etc/passwd format. Just split it on the colon : to parse out the fields.
It should be installed on most Linux systems (or any system that uses GNU Lib C (RHEL: glibc-common, Deb: libc-bin)
The title of this question is How to get $HOME directory of different user in bash script? and that is what people are coming here from Google to find out.
There is a safe way to do this!
on Linux/BSD/macOS/OSX without sudo or root
user=pi
user_home=$(bash -c "cd ~$(printf %q $USER) && pwd")
NOTE: The reason this is safe is because bash (even versions prior to 4.4) has its own printf function that includes:
%q quote the argument in a way that can be reused as shell input
See: help printf
Compare the how other answers here respond to code injection
# "ls /" is not dangerous so you can try this on your machine
# But, it could just as easily be "sudo rm -rf /*"
$ user="root; ls /"
$ printf "%q" "$user"
root\;\ ls\ /
# This is what you get when you are PROTECTED from code injection
$ user_home=$(bash -c "cd ~$(printf "%q" "$user") && pwd"); echo $user_home
bash: line 0: cd: ~root; ls /: No such file or directory
# This is what you get when you ARE NOT PROTECTED from code injection
$ user_home=$(bash -c "cd ~$user && pwd"); echo $user_home
bin boot dev etc home lib lib64 media mnt ono opt proc root run sbin srv sys tmp usr var /root
$ user_home=$(eval "echo ~$user"); echo $user_home
/root bin boot dev etc home lib lib64 media mnt ono opt proc root run sbin srv sys tmp usr var
on Linux/BSD/macOS/OSX as root
If you are doing this because you are running something as root then you can use the power of sudo:
user=pi
user_home=$(sudo -u $user sh -c 'echo $HOME')
on Linux/BSD (but not modern macOS/OSX) without sudo or root
If not, the you can get it from /etc/passwd. There are already lots of examples of using eval and getent, so I'll give another option:
user=pi
user_home=$(awk -v u="$user" -v FS=':' '$1==u {print $6}' /etc/passwd)
I would really only use that one if I had a bash script with lots of other awk oneliners and no uses of cut. While many people like to "code golf" to use the fewest characters to accomplish a task, I favor "tool golf" because using fewer tools gives your script a smaller "compatibility footprint". Also, it's less man pages for your coworker or future-self to have to read to make sense of it.
You want the -u option for sudo in this case. From the man page:
The -u (user) option causes sudo to run the specified command as a user other than root.
If you don't need to actually run it as them, you could move to their home directory with ~<user>. As in, to move into my home directory you would use cd ~chooban.
So you want to:
execute part of a bash script as a different user
change to that user's $HOME directory
Inspired by this answer, here's the adapted version of your script:
#!/usr/bin/env bash
different_user=deploy
useradd -m -s /bin/bash "$different_user"
echo "Current user: $(whoami)"
echo "Current directory: $(pwd)"
echo
echo "Switching user to $different_user"
sudo -u "$different_user" -i /bin/bash - <<-'EOF'
echo "Current user: $(id)"
echo "Current directory: $(pwd)"
EOF
echo
echo "Switched back to $(whoami)"
different_user_home="$(eval echo ~"$different_user")"
echo "$different_user home directory: $different_user_home"
When you run it, you should get the following:
Current user: root
Current directory: /root
Switching user to deploy
Current user: uid=1003(deploy) gid=1003(deploy) groups=1003(deploy)
Current directory: /home/deploy
Switched back to root
deploy home directory: /home/deploy
This works in Linux. Not sure how it behaves in other *nixes.
getent passwd "${OTHER_USER}"|cut -d\: -f 6
I was also looking for this, but didn't want to impersonate a user to simply acquire a path!
user_path=$(grep $username /etc/passwd|cut -f6 -d":");
Now in your script, you can refer to $user_path in most cases would be /home/username
Assumes: You have previously set $username with the value of the intended users username.
Source: http://www.unix.com/shell-programming-and-scripting/171782-cut-fields-etc-passwd-file-into-variables.html
Quick and dirty, and store it in a variable:
USER=somebody
USER_HOME="$(echo -n $(bash -c "cd ~${USER} && pwd"))"
I was struggling with this question because I was looking for a way to do this in a bash script for OS X, hence /etc/passwd was out of the question, and my script was meant to be executed as root, therefore making the solutions invoking eval or bash -c dangerous as they allowed code injection into the variable specifying the username.
Here is what I found. It's simple and doesn't put a variable inside a subshell. However it does require the script to be ran by root as it sudos into the specified user account.
Presuming that $SOMEUSER contains a valid username:
echo "$(sudo -H -u "$SOMEUSER" -s -- "cd ~ && pwd")"
I hope this helps somebody!
If the user doesn't exist, getent will return an error.
Here's a small shell function that doesn't ignore the exit code of getent:
get_home() {
local result; result="$(getent passwd "$1")" || return
echo $result | cut -d : -f 6
}
Here's a usage example:
da_home="$(get_home missing_user)" || {
echo 'User does NOT exist!'; exit 1
}
# Now do something with $da_home
echo "Home directory is: '$da_home'"
The output of getent passwd username can be parsed with a Bash regular expression
OTHER_HOME="$(
[[ "$(
getent \
passwd \
"${OTHER_USER}"
)" =~ ([^:]*:){5}([^:]+) ]] \
&& echo "${BASH_REMATCH[2]}"
)"
If you have sudo active, just do:
sudo su - admin -c "echo \$HOME"
NOTE: replace admin for the user you want to get the home directory

Resources