How can I get the current script name of a script running in parent script's shell? - linux

How can I get the name of a child script that is running in the shell of it's parent?
consider script1.sh;
#this is script 1
echo "$0"
. ./script2.sh
and script2.sh;
#this is script 2
echo "$0"
Since script2.sh is being executed in [sourced from] the same shell as script1.sh, the output of running these scripts are both;
./script1.sh
How can get the name of script2.sh within script2.sh?

You are sourcing a script, not running it. Therefore, you are looking for BASH_SOURCE.
$ cat test.sh
echo $BASH_SOURCE
$ . ./test.sh
./test.sh
Update:
Some people posted that BASH_SOURCE is indeed an array and, while this is actually true, I don't think you need to use all the syntax boilerplate because. "$BASH_SOURCE" (yes, you should quote your variables) will return the first element of the array, exactly what you are looking for.
Said that, it would be useful to post and example of using BASH_SOURCE as an array so here it goes:
$ cat parent.sh
#!/bin/bash
. ./child.sh
$ cat child.sh
#!/bin/bash
. ./grandchild.sh
$ cat grandchild.sh
#/bin/bash
echo "BASH_SOURCE: $BASH_SOURCE"
echo "child : ${BASH_SOURCE[0]}"
echo "parent1 : ${BASH_SOURCE[1]}"
echo "parent2 : ${BASH_SOURCE[2]}"
$ ./parent.sh
BASH_SOURCE: ./grandchild.sh
child : ./grandchild.sh
parent1 : ./child.sh
parent2 : ./parent.sh

Related

Define environment variable in a subshell with heredoc

Background: testing a bash script inside a pod where default user does not have sudo rights so that user cannot user vim or nano to create a .sh file so I have to find a way around with cat << EOF >> test.sh.
I am doing some local test to make sure it's working properly first. Locally I am creating a file test.sh with nano. See below
#!/bin/bash
# test.sh
VAR="Test"
echo $VAR
When I cat it:
#!/bin/bash
# test.sh
VAR="Test"
echo $VAR%
I save test.sh , made it execuatble chmod +x test.sh and ran it with ./test.sh The output:
Test
Now when I try to mimic the same behavior in a bash heredoc instead this is the command I use:
cat <<EOF >> test.sh
#!/bin/bash
# test.sh
VAR="Test"
echo $VAR
EOF
I saved it and made it execuatble as well. The cat output is:
#!/bin/bash
# test.sh
VAR="Test"
echo
So obviously running it wouldn't work. The output null.
I think the issue I am facing is that the environment variable $VAR is not defined properly inside the subshell using heredoc.
When you write this:
cat <<EOF >> test.sh
#!/bin/bash
# test.sh
VAR="Test"
echo $VAR
EOF
...that $VAR is expanded by your current shell (just like writing something like echo "$VAR"). If you want to suppress variable expansion inside your heredoc, you can quote it (note the quotes around 'EOF'):
cat <<'EOF' >> test.sh
#!/bin/bash
# test.sh
VAR="Test"
echo $VAR
EOF
This inhibits variable expansion just like single quotes (echo '$VAR').

Can I call an interactive script in orther bash?

For example,there is an interactive script that read user input his name.And then write the name in a file.
#! /bin/bash
read name
echo $name>>name.txt
If I can't change the interactive script , how can I use anothor bash to quote the interactive script?
For example,I want to write a bash that extracts the name from a text and then calls this interactive script.
So can I achieve my idea?
Use a pipe to combine output of one program with the input of the next program:
echo 'name from other script' | ./script1.sh
echo can be replaced with any other executable file or script:
$ cat >script2.sh
#!/bin/sh
echo 'first input'
echo 'second input'
^D
$ chmod u+x script2.sh
$ ./script2.sh | ./script1.sh
If your intended input name is already in a file, use IO redirection instead:
$ ./script2.sh < file_containing_name.txt

Check if script was started by another script [duplicate]

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 I tell a Linux Script (ash, not bash) is running "sourced"?

How can I tell in an ash script if it is running "sourced" or "normal"? By sourced I mean using the "." or "source" command to launch the script in the current shell.
Not sure if it's the best option (will not work if the script has the same name as the shell), but you can check the first parameter ($0). Example:
$ cat test.sh
#!/bin/ash
echo "Value: $0"
$ ./test.sh
Value: ./test.sh
$ source test.sh
Value: ash
If you want to check if the file was sourced, you can use something like this:
#!/bin/ash
case $0 in
ash) echo "Sourced" ;;
*) echo "Not sourced" ;;
esac

Variable scope in the shell level

Recently I have been reading The Advanced Bash Script and I find something about the variable scope between parent and children shells puzzle me so much. Here it is:
Scene:
there are some ways to spawn a child shell:
first, (command-lists);
second, execute a non-built-in command or a script, and so on.
Since when we run a script in the parent script, the child script can not see the variables in the parent shell. Why is it possible that in the (command-lists) struct the child shell can seen the variable in the parent shell.
e.g
(command-lists)
$ a=100
$ (echo $a)
100
$
run a script
$ cat b.sh
echo $a
$ a=100
$ ./b.sh
# empty
How?
In the case where you have a sub-shell run in the original script:
(command1; command2; ...)
the sub-shell is a direct copy of the original shell created by fork(), and therefore has direct access to its own copy of all the original variables available to it.
Suppose the commands (command1, command2 etc) in the sub-shell are themselves shell scripts. Those commands are executed by the sub-shell calling fork() and then exec() to create a new shell, and the new shell does not inherit the non-exported variables from the original shell.
Addressing your examples directly:
$ a=100
$ (echo $a)
100
$
Here, the sub-shell has its own copy of all the variables (specifically, a) that the parent shell had access to. Any changes made in the sub-shell will not be reflected in the parent shell, of course, so:
$ a=100
$ (echo $a; a=200; echo $a)
100
200
$ echo $a
100
$
Now your second example:
$ cat b.sh
echo $a
$ a=100
$ ./b.sh
$ . ./b.sh
100
$ source ./b.sh
100
$ a=200 ./b.sh
200
$ echo $a
100
$ export a
$ ./b.sh
100
$
The variable a is not exported, so the first time b.sh is run, it has no value for $a so it echoes an empty line. The second two examples are a 'cheat'; the shell reads the script b.sh as if it was part of the current shell (no fork()) so the variables are still accessible to b.sh, hence it echoes 100 each time. (Dot or . is the older mechanism for reading a script in the current shell; the Bourne shell in 7th Edition UNIX used it. The source command is borrowed from the C shells as an equivalent mechanism.)
The command a=200 ./b.sh exports a for the duration of the command, so b.sh sees and echoes the modified value 200 but the main shell has a unchanged. Then when a is exported, it is available to b.sh automatically, hence it sees and echoes the last 100.

Resources