cd into a specific directory changes PATH variable - bash

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.

Related

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.

In a unix box, I am taking a list of files as input. If it is found, return the path otherwise return a message "filename file not found"

I have used the find command for this, but it doesnt return any message when a file is not found.
And I want the search to be recursive and return a message "not found" when a file is not found.
Here's the code I have done so far. Here "input.txt" contains the list of files to be searched.
set `cat input.txt`
echo $#
for i in $#
do
find $HOME -name $i
done
Try this:
listfile=input.txt
exec 3>&1
find | \
grep -f <( sed 's|.*|/&$|' "$listfile" ) | \
tee /dev/fd/3 | \
sed 's|.*/\([^/]*\)$|\1|' | \
grep -v -f - "$listfile" | \
sed 's/$/ Not found/'
exec 3>&-
open file descriptor 3
find the files
see if they're on the list (use sed to
send a copy of the found ones to file descriptor 3
strip off the directory name
get a list of the ones that don't appear
add the "Not found" message
close file descriptor 3
Output looks like:
/path/to/file1
/path/somewhere/file2
foo Not found
bar Not found
No loops necessary.
Whats wrong with using a script. I hope this will do.
#!/bin/bash -f
for i in $#
do
var=`find $HOME -name $i`
if [ -z "$var"]
then
var="File not found"
fi
echo $var
done
You can use the shell builtin 'test' to test the existence of a file. There is also an alternative syntax using square brackets:
if [ -f $a ]; then # Don't forget the semicolon.
echo $a
else
echo 'Not Found'
fi
Here is one way - create a list of all the files to grep against. If your implementation supports
grep -q otherwise use grep [pattern] 2&>1 >/dev/null....
find $HOME -type f |
while read fname
do
echo "$(basename $fname) $fname"
done > /tmp/chk.lis
while read fname
do
grep -q "^$fname" /tmp/chk.lis
[ $? -eq 0 ] && echo "$fname found" || echo "$fname not found"
done < /tmp/chk.lis
All of this is needed because POSIX find does not return an error when a file is not found
perl -nlE'say-f$_?$_:"not found: $_"' file

Adding spaces to stdout

Is it possible to add spaces to the left of every output to stdout (and stderr if possible) when I run commands in a bash shell script?
I'd like to do something like:
#!/bin/bash
echo Installing: Something
echo " => installing prerequisite1"
## INSERT MAGICAL LEFT SPACES COMMAND HERE ##
apt-get install -q -y prerequisite
## ANOTHER MAGICAL CANCELLING LEFT SPACES COMMAND HERE ##
echo " => installing prerequisite2"
# ... the padding again ...
wget http://abc.com/lostzilla.tar.gz
tar vzxf lostzilla.tar.gz
cd lostzilla-1.01
./configure
make && make install
# ... end of padding ...
echo Done.
Any idea?
EDIT: Added quotes to the echo command, otherwise they won't be padded.
Yes, you can quote them for simple things:
echo ' => installing prerequisite1'
and pipe the output through sed for complex things:
tar vzxf lostzilla.tar.gz 2>&1 | sed 's/^/ /'
The 2>&1 puts stdout and stderr onto the stdout stream and the sed replaces every start-of-line marker with three spaces.
How well this will work on something like wget which does all sorts of cursor manipulations I'm not sure.
Example shown here:
pax> ls -1 p*
phase1.py
phase1.sh
phase2.py
phase2.sh
primes.c
primes.exe
primes.sh
primes.stat
pax> ls -1 p* | sed 's/^/ /'
phase1.py
phase1.sh
phase2.py
phase2.sh
primes.c
primes.exe
primes.sh
primes.stat
One trick I've used in the past is to ensure that the scripts themselves take care of the indentation:
#!/bin/bash
if [[ "${DONT_EVER_SET_THIS_VAR}" = "" ]] ; then
export DONT_EVER_SET_THIS_VAR=except_for_here
$0 | sed 's/^/ /'
exit
fi
ls -1 p*
This will re-run the script with indentation through sed if it's not already doing so. That way, you don't have to worry about changing all your output statements. A bit of a hack, I know, but I tend to just do what's necessary for quick-and-dirty shell scripts.
If you want to turn spacing on and off, use the following awk script:
#!/usr/bin/gawk -f
/^#SPACEON/ { spaces=1; }
/^#SPACEOFF/ { spaces=0; }
!/^#SPACE/ {
if(spaces) {
print " " $0;
} else {
print $0;
}
}
Note that there are slight problems with your bash scipt. Notably, the use of => in your echo statements will output the character = to the file "installing".
#!/bin/bash
echo Installing: Something
echo '=> installing prerequisite1'
echo '#SPACEON'
echo You would see apt-get install -q -y prerequisite
echo '#SPACEOFF'
echo '=> installing prerequisite2'
echo '#SPACEON'
echo You would see wget http://abc.com/lostzilla.tar.gz
echo You would see tar vzxf lostzilla.tar.gz
echo You would see cd lostzilla-1.01
echo You would see ./configure
echo You would see make \&\& make install
echo '#SPACEOFF'
echo Done.
Combining the two gives me:
$ ./do-stuff | ./magic-spacing
Installing: Something
=> installing prerequisite1
You would see apt-get install -q -y prerequisite
=> installing prerequisite2
You would see wget http://abc.com/lostzilla.tar.gz
You would see tar vzxf lostzilla.tar.gz
You would see cd lostzilla-1.01
You would see ./configure
You would see make && make install
Done.
Where do-stuff is your bash script and magic-spacing is my awk script above.
Depending on how the command writes to stdout, you can just indent with a simple awk script:
$ echo -e 'hello\nworld' | awk '{print " ",$0}'
hello
world
Quite un-magical you can use printf to do the following:
# space padding for single string
printf "%-4s%s\n" "" "=> installing prerequisite1"
# space padding for single command output
# use of subshell leaves original IFS intact
( IFS=$'\n'; printf " %s\n" $(command ls -ld * 2>&1) )
# note: output to stderr is unbuffered
( IFS=$'\n'; printf " %s\n" $(command ls -ld * 1>&2) )
It's also possible to group commands by enclosing them in curly braces and space-padd their output like so:
{
cmd1 1>&2
cmd2 1>&2
cmd3 1>&2
} 2>&1 | sed 's/.*/ &/'
It's possible to redirect stdout to stderr script/shell-wide using exec ...
(
exec 1>&2
command ls -ld *
) 2>&1 | sed 's/^/ /'
Use python pyp (The Pyed Piper):
ls -ld | pyp "' '+p"

have to determine all users home directories - tilde scripting problem

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

Why is my awk print not showing up on the terminal

I have the following script which does a "which -a" on a command then a "ls -l" to let me know if it's a link or not .. ie "grep" since I have gnu commands installed (Mac with iTerm).
#!/usr/bin/env bash
which -a $1 | xargs -I{} ls -l "{}" \
| awk '{for (i = 1; i < 9; i++) $i = ""; sub(/^ */, ""); print}'
When I run this from the script "test grep" I receive no output, but when I run it via "bash -x test grep" I receive the following:
bash -x test grep
+ which -a grep
+ xargs '-I{}' ls -l '{}'
+ awk '{for (i = 1; i < 9; i++) $i = ""; sub(/^ */, ""); print}'
/usr/local/bin/grep -> ../Cellar/grep/3.1/bin/grep
/usr/bin/grep
The last 2 lines is what I'm looking to display. Thought this would be easier to do ;-) .. I also tried appending the pipe thinking printf would fix the issue:
| while read path
do
printf "%s\n" "$path"
done
Thanks and .. Is there a better way to get what I need?
The problem is that you named your script test.
If you want to run a command that's not in your PATH, you need to specify the directory it's in, e.g. ./test.
You're not getting an error for trying to run test because there is a built-in bash command called test that is used instead. For extra confusion, the standard test produces no output.
In conclusion:
Use ./ to run scripts in the current directory.
Never call your test programs test.
Thanks for the never naming a script "test" .. old habits are hard to break (I came from a non-unix background.
I ended with the following
for i in $(which -a $1)
do
stat $i | awk NR==1{'$1 = ""; sub(/^ */, ""); print}'
done
or simpler
for i in $(which -a $1)
do
stat -c %N "$i"
done
Consider the following shell function:
cmdsrc() {
local cmd_file cmd_file_realpath
case $(type -t -- "$1") in
file) cmd_file=$(type -P -- "$1")
if [[ -L "$cmd_file" ]]; then
echo "$cmd_file is a symlink" >&2
elif [[ -f "$cmd_file" ]]; then
echo "$cmd_file is a regular file" >&2
else
echo "$cmd_file is not a symlink or a regular file" >&2
fi
cmd_file_realpath=$(readlink -- "$cmd_file") || return
if [[ $cmd_file_realpath != "$cmd_file" ]]; then
echo "...the real location of the executable is $cmd_file_realpath" >&2
fi
;;
*) echo "$1 is not a file at all: $(type -- "$1")" >&2 ;;
esac
}
...used as such:
$ cmdsrc apt
/usr/bin/apt is a symlink
...the real location of the executable is /System/Library/Frameworks/JavaVM.framework/Versions/A/Commands/apt
$ cmdsrc ls
/bin/ls is a regular file
$ cmdsrc alias
alias is not a file at all: alias is a shell builtin
Took some suggestions and came up with the following:
The prt-underline is just a fancy printf function. I decided not to go with readline since the ultimate command resolution may be unfamiliar to me and I only deal with regular files .. so does't handle every situation but in the end gives me the output I was looking for. Thanks for all the help.
llt ()
{
case $(type -t -- "$1") in
function)
prt-underline "Function";
declare -f "$1"
;;
alias)
prt-underline "Alias";
alias "$1" | awk '{sub(/^alias /, ""); print}'
;;
keyword)
prt-underline "Reserved Keyword"
;;
builtin)
prt-underline "Builtin Command"
;;
*)
;;
esac;
which "$1" &> /dev/null;
if [[ $? = 0 ]]; then
prt-underline "File";
for i in $(which -a $1);
do
stat "$i" | awk 'NR==1{sub(/^ File: /, ""); print}';
done;
fi
}

Resources