How to find a usable executable file in bash - bash

I have written this little script to find executables that are passed as arguments e.g.
./testexec du ls md
How do I get the script to not output commands that are not found - e.g. not to display error output for "md" command ??
#!/bin/sh
for filename in "$#"
do
which $filename
done

If you are using bash, you should use the builtin "type" rather than the external utility "which". The type command will return a non-zero exit status if the command is not found, which makes it easy to use with a conditional.
for filename in "$#"; do
if type -P "$filename" >/dev/null; then
echo "found in PATH: $filename"
fi
done

Just redirect the error message (coming from stderr) into /dev/null:
which $filename 2>/dev/null

Related

Getting status when launching an alias in bash

I've figured out how to use aliases from within a bash script. They do get executed, but I don't get the result back in the variable "Status". All following types of launching an alias get the result back when used interactively, but "Status" stay empty in the script no matter if I use "shopt -s expand_aliases" or not:
$ Status=`start_ETAP-TestCluster 2>/dev/null`
$ echo $Status
{"JobIds":["8c54392c-363c-4e28-827e-a1384da7fd37"]}
$ Status=`${BASH_ALIASES[start_NetIntel-Cluster]} 2>/dev/null`
$ echo $Status
{"JobIds":["dd05f5cb-a490-4fe3-b4fd-be61f62bbb88"]}
$ eval Status=`${BASH_ALIASES[start_NetIntel-Cluster]} 2>/dev/null`
$ echo $Status
{JobIds:[25a246ab-e5f5-4d65-80ea-4ff5e15cc02c]}
$ eval $(echo Status=`${BASH_ALIASES[start_NetIntel-Cluster]} 2>/dev/null`)
$ echo $Status
{JobIds:[4ce2f4e5-0599-47f5-b7f4-cd2ec1de9909]}
Has anyone some hints and ideas how to get the result in the variable "Status" within a bash script ?
The command between back quotes, or betwen $( and ) is expanded by the shell to the ouput of command. The exit status can be retrieved with $? special variable.
output=$(command ) # output=`command ` #old syntax
status=$?
Otherwise standard output and error can be redirected to files.
command 1> output_file 2> error_file
status=$?
Also if only the exit status success is wanted the status can be checked with logical operators (exit status ==0 means success and logical value true)
command 1> output_file 2> error_file || {
echo failed
exit 1
}
Thank you for your help and hints. The main problem was that expanding the alias (like start_ETAP-Cluster) did not work properly. Thus using following in the script did help:
shopt -s expand_aliases
source ~/.bash_aliases
Using the $Status in my code was misleading, I changed it to $JobID, which reflects better what I wanted. I'm using web hooks in the aliases, which return a JSON Object with the job id. Furthermore assigning $Status=$? works also. Now the code does its work from within the script:
$ JobID=$( ${BASH_ALIASES[start_ETAP-Cluster]} 2>/dev/null )
$ echo $JobID
{"JobIds":["8c54392c-363c-4e28-827e-a1384da7fd37"]}
Next step will be to move the web hook calls from the aliases to bash functions, will give me more flexibility then aliases.

Executing a shell script from a file

My OS platform is this : SunOS machinehull01 5.10 Generic_148888-05 sun4v sparc SUNW,Sun-Fire-T200
I have written a shell script to run from a file
File name: test.sh
#!/bin/sh
VARNAME=$grep '-l' TestWord /home/hull/xml/text/*.txt
echo "Found $VARNAME"
When I run the above command in the console I'm getting the correct output without errors, But when I run sh test.sh or ./test.sh I'm getting below error
test.sh: -l: not found
Found
Can someone please help me on this?
You are searching for so called "command substitution" :
VARNAME=$(grep -l TestWord /home/hull/xml/text/*.txt)
echo "Found $VARNAME"
It will execute the command between $( and the closing parenthesis ) in a subshell and return the output of the command into VARNAME.
Got it.
#!/bin/sh
VARNAME=`grep -l TestWord /home/hull/xml/text/*.txt`
echo "Found $VARNAME"
I had to put those (`)there.

Get the name of the caller script in bash script

Let's assume I have 3 shell scripts:
script_1.sh
#!/bin/bash
./script_3.sh
script_2.sh
#!/bin/bash
./script_3.sh
the problem is that in script_3.sh I want to know the name of the caller script.
so that I can respond differently to each caller I support
please don't assume I'm asking about $0 cause $0 will echo script_3 every time no matter who is the caller
here is an example input with expected output
./script_1.sh should echo script_1
./script_2.sh should echo script_2
./script_3.sh should echo user_name or root or anything to distinguish between the 3 cases?
Is that possible? and if possible, how can it be done?
this is going to be added to a rm modified script... so when I call rm it do something and when git or any other CLI tool use rm it is not affected by the modification
Based on #user3100381's answer, here's a much simpler command to get the same thing which I believe should be fairly portable:
PARENT_COMMAND=$(ps -o comm= $PPID)
Replace comm= with args= to get the full command line (command + arguments). The = alone is used to suppress the headers.
See: http://pubs.opengroup.org/onlinepubs/009604499/utilities/ps.html
In case you are sourceing instead of calling/executing the script there is no new process forked and thus the solutions with ps won't work reliably.
Use bash built-in caller in that case.
$ cat h.sh
#! /bin/bash
function warn_me() {
echo "$#"
caller
}
$
$ cat g.sh
#!/bin/bash
source h.sh
warn_me "Error: You did not do something"
$
$ . g.sh
Error: You did not do something
g.sh
$
Source
The $PPID variable holds the parent process ID. So you could parse the output from ps to get the command.
#!/bin/bash
PARENT_COMMAND=$(ps $PPID | tail -n 1 | awk "{print \$5}")
Based on #J.L.answer, with more in depth explanations, that works for linux :
cat /proc/$PPID/comm
gives you the name of the command of the parent pid
If you prefer the command with all options, then :
cat /proc/$PPID/cmdline
explanations :
$PPID is defined by the shell, it's the pid of the parent processes
in /proc/, you have some dirs with the pid of each process (linux). Then, if you cat /proc/$PPID/comm, you echo the command name of the PID
Check man proc
Couple of useful files things kept in /proc/$PPID here
/proc/*some_process_id*/exe A symlink to the last executed command under *some_process_id*
/proc/*some_process_id*/cmdline A file containing the last executed command under *some_process_id* and null-byte separated arguments
So a slight simplification.
sed 's/\x0/ /g' "/proc/$PPID/cmdline"
If you have /proc:
$(cat /proc/$PPID/comm)
Declare this:
PARENT_NAME=`ps -ocomm --no-header $PPID`
Thus you'll get a nice variable $PARENT_NAME that holds the parent's name.
You can simply use the command below to avoid calling cut/awk/sed:
ps --no-headers -o command $PPID
If you only want the parent and none of the subsequent processes, you can use:
ps --no-headers -o command $PPID | cut -d' ' -f1
You could pass in a variable to script_3.sh to determine how to respond...
script_1.sh
#!/bin/bash
./script_3.sh script1
script_2.sh
#!/bin/bash
./script_3.sh script2
script_3.sh
#!/bin/bash
if [ $1 == 'script1' ] ; then
echo "we were called from script1!"
elsif [ $1 == 'script2' ] ; then
echo "we were called from script2!"
fi

How can a ksh script determine the full path to itself, when sourced from another?

How can a script determine it's path when it is sourced by ksh? i.e.
$ ksh ". foo.sh"
I've seen very nice ways of doing this in BASH posted on stackoverflow and elsewhere but haven't yet found a ksh method.
Using "$0" doesn't work. This simply refers to "ksh".
Update: I've tried using the "history" command but that isn't aware of the history outside the current script.
$ cat k.ksh
#!/bin/ksh
. j.ksh
$ cat j.ksh
#!/bin/ksh
a=$(history | tail -1)
echo $a
$ ./k.ksh
270 ./k.ksh
I would want it echo "* ./j.ksh".
If it's the AT&T ksh93, this information is stored in the .sh namespace, in the variable .sh.file.
Example
sourced.sh:
(
echo "Sourced: ${.sh.file}"
)
Invocation:
$ ksh -c '. ./sourced.sh'
Result:
Sourced: /var/tmp/sourced.sh
The .sh.file variable is distinct from $0. While $0 can be ksh or /usr/bin/ksh, or the name of the currently running script, .sh.file will always refer to the file for the current scope.
In an interactive shell, this variable won't even exist:
$ echo ${.sh.file:?}
-ksh: .sh.file: parameter not set
I believe the only portable solution is to override the source command:
source() {
sourced=$1
. "$1"
}
And then use source instead of . (the script name will be in $sourced).
The difference of course between sourcing and forking is that sourcing results in the invoked script being executed within the calling process. Henk showed an elegant solution in ksh93, but if, like me, you're stuck with ksh88 then you need an alternative. I'd rather not change the default ksh method of sourcing by using C-shell syntax, and at work it would be against our coding standards, so creating and using a source() function would be unworkable for me. ps, $0 and $_ are unreliable, so here's an alternative:
$ cat b.sh ; cat c.sh ; ./b.sh
#!/bin/ksh
export SCRIPT=c.sh
. $SCRIPT
echo "PPID: $$"
echo "FORKING c.sh"
./c.sh
If we set the invoked script in a variable, and source it using the variable, that variable will be available to the invoked script, since they are in the same process space.
#!/bin/ksh
arguments=$_
pid=$$
echo "PID:$pid"
command=`ps -o args -p $pid | tail -1`
echo "COMMAND (from ps -o args of the PID): $command"
echo "COMMAND (from c.sh's \$_ ) : $arguments"
echo "\$SCRIPT variable: $SCRIPT"
echo dirname: `dirname $0`
echo ; echo
Output is as follows:
PID:21665
COMMAND (from ps -o args of the PID): /bin/ksh ./b.sh
COMMAND (from c.sh's $_ ) : SCRIPT=c.sh
$SCRIPT variable: c.sh
dirname: .
PPID: 21665
FORKING c.sh
PID:21669
COMMAND (from ps -o args of the PID): /bin/ksh ./c.sh
COMMAND (from c.sh's $_ ) : ./c.sh
$SCRIPT variable: c.sh
dirname: .
So when we set the SCRIPT variable in the caller script, the variable is either accessible from the sourced script's operands, or, in the case of a forked process, the variable along with all other environment variables of the parent process are copied for the child process. In either case, the SCRIPT variable can contain your command and arguments, and will be accessible in the case of both sourcing and forking.
You should find it as last command in the history.

Shell script works in bash but not in ksh

I need to write a script to test if the command blablabla exists in the classpath. So I wrote the following code:
if ! hash blablabla >/dev/null 2>&1; then
echo not found
fi
This works fine when the script is executed in the bash. But if I try it in KSH, then it doesn't work:
#! /usr/bin/ksh
if ! hash blablabla >/dev/null 2>&1; then
echo not found
fi
I expect the echo not found to be executed but instead I get nothing. What's the problem?
I believe command is portable (if that matters):
command -v -- some_command >/dev/null 2>&1 ||
printf '%s\n' "not found"
In bash hash is a builtin command. In ksh it's an alias; aliases aren't active in shell scripts.
alias hash='alias -t --'
Try the which command, which is an external command and therefore shell-independent:
if ! which -s blablabla; then
echo not found >&2
fi
Tha hash command is a shell built-in command in bash, but not in ksh. You might want to use whence instead.
if ! whence blah; then print urgh; fi

Resources