have to determine all users home directories - tilde scripting problem - bash

Assume someuser has a home directory /home/someuser
NAME=someuser
In bash - what expression to I use combining tilde (~) and $NAME to return the users home directory?
HOMEDIRECTORY=~someuser
echo $HOMEDIRECTORY
/home/someuser
NAME=someuser
echo ~$NAME
~someuser
any suggestions?

Safer:
eval HOMEDIRECTORY="$(printf "~%q" "$NAME")"
Here the %q option to printf quotes and escapes dangerous characters.
If $NAME is joe, you'd get something like /home/joe. For root, you might get /root. For "abc;rm something" you'd get "~abc;rm something" instead of having something removed.

If you have access to getent:
getent passwd "$NAME" | cut -d: -f 6

Tilde ( ~ ) it's the same as $HOME so, not all the user will have as root to home the same directory.
But if you insist in using the tilde this do the work:
echo ~/../$NAME
See:
$ pwd
/home/oreyes
$ export NAME=john
$ export DIRECTORYNAME=~/../$NAME
$ cd $DIRECTORYNAME
$ pwd
/home/john

Interesting difference between bash and csh, where ~$VARNAME actually does what you'd expect!
This is ugly, but it seems to work in bash:
homedir=`eval "echo ~$USERNAME"`
Now $homedir holds the home directory associated with $USERNAME.

BEST METHOD
Required: nothing
(n.b., this is the same technique as getent without requiring getent)
home() { # returns empty string on invalid user
grep "^$1:" /etc/passwd | cut -d ':' -f 6
}
# grep "^$user:" /etc/passwd | cut -d ':' -f 6
/var/lib/memcached
NICE METHOD FOR ROOT LINUX
Required: Linux, root (or sudo)
home() { # returns errorlevel 1 on invalid user
su "$1" -s '/bin/sh' -c 'echo $HOME'
}
# su memcached -s '/bin/sh' -c 'echo $HOME'
/var/lib/memcached
SOLUTION FOR COMPLETE EXPANSION
magic() { # returns unexpanded tilde express on invalid user
local _safe_path; printf -v _safe_path "%q" "$1"
eval "ln -sf $_safe_path /tmp/realpath.$$"
readlink /tmp/realpath.$$
rm -f /tmp/realpath.$$
}
Example usage:
$ magic ~nobody/would/look/here
/var/empty/would/look/here
$ magic ~invalid/this/will/not/expand
~invalid/this/will/not/expand
METHOD FOR HARNESSING CSH
This is a BASH script, it just calls csh.
Required: csh
home() { # return errorlevel 1 on invalid user
export user=$1; csh -c "echo ~$user"
}
$ export user=root; csh -c "echo ~$user"
/var/root
$ export user=nodfsv; csh -c "echo ~$user"
Unknown user: nodfsv.
METHOD OF DESPERATION
Required: finger (deprecated)
home() {
finger -m "$1" |
grep "^Directory:" |
sed -e 's/^Directory: //' -e 's/ .*//'
}
# finger -m "haldaemon" |
> grep "^Directory:" |
> sed -e 's/^Directory: //' -e 's/ .*//'
/home/haldaemon
You can combined the grep operation into sed, but since this method is sucky, I wouldn't bother.

one alternative way
awk -F":" '{print "user: "$1", Home directory is: "$6}' /etc/passwd

Related

cd into a specific directory changes PATH variable

When I change directory (cd) into one specific directory (which contains a rails application) it changes the PATH variable, removing many directories from PATH including /bin, /usr/bin, and /usr/local/bin, after which I get many bash errors of "command not found" for commands like which, ls, grep, etc. It appears to be only that one directory which causes the problem, when I cd into it or cd from it. I don't see any obvious files in the directory which would be changing the PATH variable in this way. Before I enter the directory, my PATH looks like this:
$ echo $PATH
/usr/local/opt/node#10/bin:/usr/local/opt/postgresql#9.4/bin:/Users/cedric/.rbenv/shims:/Users/cedric/.rbenv/shims:/Users/cedric/.rvm/gems/ruby-2.3.7/bin:/Users/cedric/.rvm/gems/ruby-2.3.7#global/bin:/Users/cedric/.rvm/rubies/ruby-2.3.7/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/cedric/.rvm/bin:/usr/local/bin:/usr/bin:/usr/sbin:/bin:/sbin:/usr/local/bin/AWS_Tools/AutoScaling-1.0.61.6/bin:/usr/local/bin/AWS_Tools/CloudWatch-1.0.20.0/bin:/usr/local/bin/AWS_Tools/AWS-ElasticBeanstalk-CLI-2.6.3/api/:/usr/local/bin/AWS_Tools/AWS-ElasticBeanstalk-CLI-2.6.3/AWSDevTools/:/usr/local/bin/AWS_Tools/AWS-ElasticBeanstalk-CLI-2.6.3/eb/:/usr/local/bin/AWS_Tools/AWS-ElasticBeanstalk-CLI-2.6.3/eb/linux/python2.7/:/usr/local/bin/AWS_Tools/AWS-ElasticBeanstalk-CLI-2.6.3/eb/macosx/python2.7/:/usr/local/bin/AWS_Tools/AWS-ElasticBeanstalk-CLI-2.6.3/eb/macosx/python2.7/eb:/usr/local/mysql/bin:/Users/cedric/.rbenv/bin:/Users/cedric/.rbenv/shims:/usr/local/mysql/bin
After I enter the directory, my PATH looks like this:
cedric#Computer togo [develop] $ cd .
sed: illegal option -- r
usage: sed script [-Ealn] [-i extension] [file ...]
sed [-Ealn] [-i extension] [-e script] ... [-f script_file] ... [file ...]
-bash: cat: command not found
-bash: cat: command not found
-bash: cut: command not found
cedric#Computer togo $ echo $PATH
/Users/cedric/.rvm/gems/ruby-2.3.7#togo/bin:/Users/cedric/.rvm/gems/ruby-2.3.7#global/bin:/Users/cedric/.rvm/rubies/ruby-2.3.7/bin:/Users/cedric/.rvm/bin:
-bash: cut: command not found
cedric#Computer togo $
This wasn't a problem before today. Any thoughts on what could be causing this?
Thank you
RESPONSES TO COMMENTS:
$ echo "$PROMPT_COMMAND"
update_terminal_cwd;
$ type update_terminal_cwd
update_terminal_cwd is a function
update_terminal_cwd ()
{
local SEARCH=' ';
local REPLACE='%20';
local PWD_URL="file://$HOSTNAME${PWD//$SEARCH/$REPLACE}";
printf '\e]7;%s\a' "$PWD_URL"
}
(seems like that's just replacing "%20" for " " whatever PWD_URL is.)
$ type cd returns
cd is a function
cd ()
{
__zsh_like_cd cd "$#"
}
and
$ type __zsh_like_cd
__zsh_like_cd is a function
__zsh_like_cd ()
{
\typeset __zsh_like_cd_hook;
if builtin "$#"; then
for __zsh_like_cd_hook in chpwd "${chpwd_functions[#]}";
do
if \typeset -f "$__zsh_like_cd_hook" > /dev/null 2>&1; then
"$__zsh_like_cd_hook" || break;
fi;
done;
true;
else
return $?;
fi
}
In the output of set I find:
chpwd_functions=([0]="__rvm_cd_functions_set")
...
__rvm_after_cd ()
{
\typeset rvm_hook;
rvm_hook="after_cd";
if [[ -n "${rvm_scripts_path:-}" || -n "${rvm_path:-}" ]]; then
source "${rvm_scripts_path:-$rvm_path/scripts}/hook";
fi
}
...
__rvm_cd_functions_set ()
{
__rvm_do_with_env_before;
if [[ -n "${rvm_current_rvmrc:-""}" && "$OLDPWD" == "$PWD" ]]; then
rvm_current_rvmrc="";
fi;
__rvm_project_rvmrc 1>&2 || true;
__rvm_after_cd || true;
__rvm_do_with_env_after;
return 0
}
There was a lot of output for set | grep sed (48 lines) so I searched for such output with PATH:
$ set | grep sed | grep -i path
\command \cat "$environment_file_path" | __rvm_grep -Eo "[^ ]+=[^;]+" | __rvm_sed -e 's/\$PATH/'"${PATH//\//\\/}"'/' -e 's/\${PATH}/'"${PATH//\//\\/}"'/';
rvm_silence_logging=1 "$rvm_scripts_path/gemsets" list strings | __rvm_sed "s/ (default)//; s/^/$current_ruby${rvm_gemset_separator:-#}/ ; s/#default// ;"
__rvm_sed -e 's/#.*$//g' -e 's#\[##g' -e 's#\]##g' < "$rvm_path/config/known" | sort -r | uniq;
<log>Usually this is caused by shell initialization files. Search for <code>PATH=...</code> entries.
PATH="$(\sed -E -e 's#/+#/#g' -e 's#/:#:#' <<<$PATH)";
PATH="$(\sed -r -e 's#/+#/#g' -e 's#/:#:#' <<<$PATH)";
__rvm_sed_i "$__rvmrc_warning_path" -e "\#^${1}\$# d" -e '/^$/ d';
__rvm_sed_i "$__rvmrc_warning_path" -e "\#/${__rvmrc_type}\$# d" -e '\#^$# d';
__rvm_sed_i "$__rvmrc_warning_path" -e "\#^${1}\$# d" -e '\#^$# d';
Also, searching for places where PATH is set, it seems like they are all including the original value of PATH in the new value:
$ set | grep "\<PATH *="
PATH=/usr/local/opt/node#10/bin:/usr/local/opt/postgresql#9.4/bin:/Users/cedric/.rbenv/shims:/Users/cedric/.rbenv/shims:/Users/cedric/.rvm/gems/ruby-2.3.7/bin:/Users/cedric/.rvm/gems/ruby-2.3.7#global/bin:/Users/cedric/.rvm/rubies/ruby-2.3.7/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/cedric/.rvm/bin:/usr/local/bin:/usr/bin:/usr/sbin:/bin:/sbin:/usr/local/bin/AWS_Tools/AutoScaling-1.0.61.6/bin:/usr/local/bin/AWS_Tools/CloudWatch-1.0.20.0/bin:/usr/local/bin/AWS_Tools/AWS-ElasticBeanstalk-CLI-2.6.3/api/:/usr/local/bin/AWS_Tools/AWS-ElasticBeanstalk-CLI-2.6.3/AWSDevTools/:/usr/local/bin/AWS_Tools/AWS-ElasticBeanstalk-CLI-2.6.3/eb/:/usr/local/bin/AWS_Tools/AWS-ElasticBeanstalk-CLI-2.6.3/eb/linux/python2.7/:/usr/local/bin/AWS_Tools/AWS-ElasticBeanstalk-CLI-2.6.3/eb/macosx/python2.7/:/usr/local/bin/AWS_Tools/AWS-ElasticBeanstalk-CLI-2.6.3/eb/macosx/python2.7/eb:/usr/local/mysql/bin:/Users/cedric/.rbenv/bin:/Users/cedric/.rbenv/shims:/usr/local/mysql/bin
PATH="$2:$PATH"
PATH="$PATH:$2"
PATH="${rvm_user_path_prefix}:$PATH";
PATH="$PATH:${rvm_bin_path}";
PATH="${rvm_bin_path}:$PATH";
printf "%b" "export PATH=\"${__path}:\$PATH\"\n" > "$file_name";
PATH="${_new_path[*]}:$PATH";
env | __rvm_grep -E '^GEM_HOME=|^GEM_PATH=|^PATH=';
<log>Usually this is caused by shell initialization files. Search for <code>PATH=...</code> entries.
PATH="${save_PATH}";
PATH="$(\sed -E -e 's#/+#/#g' -e 's#/:#:#' <<<$PATH)";
PATH="$(\sed -r -e 's#/+#/#g' -e 's#/:#:#' <<<$PATH)";
command_to_run=(/usr/bin/env PATH="${PATH}${missing_paths}" "${command_to_run[#]}");
PATH="${_OLD_VIRTUAL_PATH}";
PATH="${__path_prefix:-}${__path_prefix:+:}$__save_PATH${__path_suffix:+:}${__path_suffix:-}";
Does any of the above raise red flags as a possible culprit? Should I be checking for something more specific?
RESPONSE TO #Roadowl (in that directory):
$ /bin/ls -la -- -*
ls: -*: No such file or directory
chruby can do this, using preexec_function in zsh and trap in bash. My answer from https://github.com/postmodern/chruby/blob/master/share/chruby/auto.sh:
You're probably sourcing https://github.com/postmodern/chruby/blob/master/share/chruby/auto.sh
which includes a trap command in bash and a preexec_function command in zsh to run chruby_auto when you change directory.
What chruby_auto does is look for the .ruby_version file you have and set ruby from that file by changing path variables.

Why I am not getting a value when i call a function within another in a bash script

I have a function that generates a random file name
#generate random file names
get_rand_filename() {
if [ "$ASCIIONLY" == "1" ]; then
for ((i=0; i<$((MINFILENAMELEN+RANDOM%MAXFILENAMELEN)); i++)) {
printf \\$(printf '%03o' ${AARR[RANDOM%aarrcount]});
}
else
# no need to escape double quotes for filename
cat /dev/urandom | tr -dc '[ -~]' | tr -d '[$></~:`\\]' | head -c$((MINFILENAMELEN+RANDOM%MAXFILENAMELEN)) #| sed 's/\(["]\)/\\\1/g'
fi
printf "%s" $FILEEXT
}
export -f get_rand_filename
When I call it from within another function
cf(){
fD=$1
echo "the target dir recieved is " $fD
CFILE="$(get_rand_filename)"
echo "the file name is "$CFILE
}
export -f cf
when I call
echo "$targetdir" | xargs -0 sh -c 'cf $1' sh
I only get the FILEXT (no random file name)
when I call
cf "$targetdir"
I get a valid result
I need to be able to handle spaces in the $targetdir and file name string.
echo "$targetdir" | xargs -0 sh -c 'cf $1' sh
You should invoke bash rather than sh. Function exporting is a bash feature.
$ foo() { echo bar; }
$ export -f foo
$ sh -c 'foo'
sh: 1: foo: not found
$ bash -c 'foo'
bar
Also, get rid of the -0 option since the input isn't NUL-separated. Use -d'\n' instead. And quote "$1" for robustness.
echo "$targetdir" | xargs -d'\n' bash -c 'cf "$1"' bash
Actually, you could use -0 if you change the input format.
printf '%s\0' "$targetdir" | xargs -0 bash -c 'cf "$1"' bash
For what it's worth, mktemp creates random temporary files, and does it safely. It makes sure the file doesn't already exist and then creates it to prevent anybody else from snatching up the name in the split second between the name being generated and it being returned to the caller.

Set a command to a variable in bash script problem

Trying to run a command as a variable but I am getting strange results
Expected result "1" :
grep -i nosuid /etc/fstab | grep -iq nfs
echo $?
1
Unexpected result as a variable command:
cmd="grep -i nosuid /etc/fstab | grep -iq nfs"
$cmd
echo $?
0
It seems it returns 0 as the command was correct not actual outcome. How to do this better ?
You can only execute exactly one command stored in a variable. The pipe is passed as an argument to the first grep.
Example
$ printArgs() { printf %s\\n "$#"; }
# Two commands. The 1st command has parameters "a" and "b".
# The 2nd command prints stdin from the first command.
$ printArgs a b | cat
a
b
$ cmd='printArgs a b | cat'
# Only one command with parameters "a", "b", "|", and "cat".
$ $cmd
a
b
|
cat
How to do this better?
Don't execute the command using variables.
Use a function.
$ cmd() { grep -i nosuid /etc/fstab | grep -iq nfs; }
$ cmd
$ echo $?
1
Solution to the actual problem
I see three options to your actual problem:
Use a DEBUG trap and the BASH_COMMAND variable inside the trap.
Enable bash's history feature for your script and use the hist command.
Use a function which takes a command string and executes it using eval.
Regarding your comment on the last approach: You only need one function. Something like
execAndLog() {
description="$1"
shift
if eval "$*"; then
info="PASSED: $description: $*"
passed+=("${FUNCNAME[1]}")
else
info="FAILED: $description: $*"
failed+=("${FUNCNAME[1]}")
done
}
You can use this function as follows
execAndLog 'Scanned system' 'grep -i nfs /etc/fstab | grep -iq noexec'
The first argument is the description for the log, the remaining arguments are the command to be executed.
using bash -x or set -x will allow you to see what bash executes:
> cmd="grep -i nosuid /etc/fstab | grep -iq nfs"
> set -x
> $cmd
+ grep -i nosuid /etc/fstab '|' grep -iq nfs
as you can see your pipe | is passed as an argument to the first grep command.

Bash function argument parsing

I want to have a function I can call from the command line that takes the following:
$ command_name /some/path/file.java
and turns into the following call:
command /some/path:file
So basically the part I'm having trouble with is substituting a : for the last / and stripping the file extension.
It's not 100% clear what you question is. Do you want a bash function or a bash script? Splitting paths and files is easily done with the commands basename and dirname.
e.g.:
$ dirname /path/to/file.txt
/path/to
$ basename /path/to/file.txt
file.txt
But if you must do it with a regex, sed works well:
$ echo /path/to/file.txt | sed "s/.*\///"
file.txt
$ echo /path/to/file.txt | sed -r "s/(.+)\/.+/\1/"
/path/to
First a script:
#! /usr/bin/env bash
COMMAND="/bin/echo"
JAVA="$1"
path=`dirname "$JAVA"`
file=`basename "$JAVA"`
exec "$COMMAND" "$path:$file"
And now a function:
fnA()
{
COMMAND="$1"
JAVA="$2"
path=`dirname "$JAVA"`
file=`basename "$JAVA"`
exec "$COMMAND" "$path:$file"
}
Assuming that the original path string,
/some/path/file.java
is passed to your script as $1, you get the modified string as
path_string_with_colon=$(dirname $1):$(basename $1 .java)

Parse file to .aliasrc

I want to transform a string given in this form:
xyx some commands
into this form:
alias xyz="some commands"
I tried different combinations in the terminal. It seems (i'm not sure) that it worked once, but never when i run this from the script. I've read somewhere that this is a variable problem.
Alias for readability:
alias first="sed 's/\s.*//'"
alias rest="sed 's/\S*\s*//'"
cat f_in | tee -a >(one=$(first)) >(two=$(rest)) | tee >(awk '{print "alias "$1"=\""$2"\""}' > f_out )
I used awk in this way to parse "cat f_in" into "print". It doesn't work. Then, i used "awk -v" but it still doesn't work too. How to redirect variable $one and $two into awk:
{one=$(first) === first | read -r one }?
Is this what you're trying to do:
$ echo 'xyx some commands' |
awk '{var=$1; sub(/^[^[:space:]]+[[:space:]]+/,""); printf "alias %s=\"%s\"\n", var, $0}'
alias xyx="some commands"
$ echo 'xyx some commands' |
sed 's/\([^[:space:]]*\)[[:space:]]*\(.*\)/alias \1="\2"/'
alias xyx="some commands"

Resources