I think that the answer is NO.
If I've understood correctly, what you would like to do is create a user_entry
variable, so you can write:
echo "try '$user_entry -s $@' instead"
I'll talk about why I think you can't below, but first, let's try to understand the behavior. Very briefly, $0
is the command given to the sub-shell which executes the script.
The reason that what you describe as happening is happening is that executing a script runs the script in a sub-shell. In this sub-shell, the arguments that become $0
, $1
, $2
are resolved and finalized. To see this, try the following. Here is a program I wrote that will show the concepts that I hope will explain things. (It will also take care of the "additional flag" stuff you describe.)
Go to a directory that is NOT in your PATH
, and save the following program. I called it try_it.sh
#/bin/bash
echo "Welcome to the program!"
#print out all of the arguments as given in the subshell
for (( i=0;i<=$#;i++ )); do
echo "Argument ${i} is: ${!i}"
# bash's inderection feature, allows $0, $1, $2, ...
done #endof: for (( i=0;i<=$#;i++ ))
if [[ $1 != "-s" ]] ; then
echo "try '$0 -s $@' instead"
fi #endof: if [[ $1 != "-s" ]]
echo "Working directory: $(pwd)"
echo '$'"0 = $0"
echo "Everything before the script name and last / : "\
'${'"0"'%/*}'" = "${0%/*}
echo "Just the script name: "'${'"0"'##*/}'" = "${0##*/}
while true; do
echo "Hit Ctrl+C to stop this program's execution."
sleep 10000 # infinite loop, with instructions on how to get out
done #endof: while true
Run chmod +x try_it.sh
to make sure it's executable.
Now, if possible (it will make things a lot easier to see), close all the shells (terminals).
Open a new terminal (we'll call it Terminal1) and type ps
. I hope it makes sense to write the following as another way of describing the exact same action:
Terminal1> ps
This ps
tells you what processes are running on the Linux kernel. You should see one bash
and one ps
in the output. The 1 bash
in the output tells you that 1 shells are open, which I hope you can see from your screen.
Open a second terminal, we'll call it Terminal2.
Go back to Terminal1 and type
Terminal1> ps
You should now see two bash
s and one ps
in the output. (There could be other things, but there are no more than two bash
s and no less.) The 2 bash
s tell you that 2 shells are open, which I hope you can see from your screen.
Okay. Let's figure out the process of $0
. I'm not going to put all of the output here. Hopefully, in seeing the output on your screen (as you try running the script as described,) you can figure out how to create your user_entry
variable. I don't actually think you can do this, but I'll try to let you know what's going on.
Let's go to Terminal2. Change directories to the location of your try_it.sh
script.
Terminal2> cd /path/to/script/
Run the script (in Terminal2) as
Terminal2> ./try_it.sh 1 2 3
Here's what I saw:
$ ./try_it.sh 1 2 3
Welcome to the program!
Argument 0 is: ./try_it.sh
Argument 1 is: 1
Argument 2 is: 2
Argument 3 is: 3
try './try_it.sh -s 1 2 3' instead
Working directory: /home/me/other_dir
$0 = ./try_it.sh
Everything before the script name and last / : ${0%/*} = .
Just the script name: ${0##*/} = try_it.sh
Hit Ctrl+C to stop this program's execution.
(Don't push "Ctrl+C" just yet.) Note that my /path/to/script/
is /home/me/other_dir/
Return to Terminal1 and run ps
again.
Terminal1> ps
You should see 3 bash
s. You have your 2 open shells and one sub-shell. Note that the infinite loop is allowing you to "see" the sub-shell. Also, the sleep
that you see in the output of ps
keeps the "Hit Ctrl+C to stop this program's execution." from repeating on the screen.
Here, we can see that the command "given" to the sub-shell was ./try_it.sh
. $0
is the command given to the sub-shell.
Okay, Ctrl+C
on Terminal2. Go ahead and ps
on Terminal1.
Terminal1> ps
Only 2 bash
s. The sub-shell has been closed.
Now, try the following:
Terminal2> /path/to/script/try_it.sh 1 2 3
Terminal1> ps
Number of bash
s: 3; command given to sub-shell: /path/to/script/try_it.sh
Try this:
Terminal2> try_it.sh 1 2 3
You get an error. When the path to the executable has not been added to PATH
, there needs to be a relative or absolute path to the script for it to execute.
Let's put our script in the PATH
. For me (on Cygwin), this is the next command. Make sure you know the correct way to add to PATH
on your system. Don't just use this one without checking!
export PATH="$(pwd):$PATH"
Don't worry, this will be gone from your path the next time you close Terminal2.
Now, while you are still in /path/to/script
, run
Terminal2> try_it.sh
Terminal1> ps
Number of bash
s: 3; command given to sub-shell: /path/to/script/try_it.sh
The "magic of Linux" has gone through the directories in PATH
, looking for one that had an executable try_it.sh
. The Linux magic connects the correct directory and the script name, then hands that full command to the sub-shell.
Let's make another directory in /path/to/script/
Terminal2> mkdir another_directory
Terminal2> cd another_directory
Let's run it with a relative path.
Terminal2> ../try_it.sh 1 2 3
Terminal1> ps
Number of bash
s: 3; command given to sub-shell: ../try_it.sh
A crazier relative path. My /path/to/script/
was really /home/me/other_dir/
, so I'll do a crazy relative path as follows, noting that I'm in the another_directory
directory.
Terminal2> ../../other_dir/another_directory/../try_it.sh 1 2 3
Terminal1> ps
Number of bash
s: 3; command given to sub-shell: ../../other_dir/another_directory/../try_it.sh
We've put the script's path into PATH
, so let's try the following from another_directory
Terminal2> try_it.sh
Terminal1> ps
Number of bash
s: 3; command given to sub-shell: /path/to/script/try_it.sh
Now, an absolute path. This is run from another_directory
Terminal2> /path/to/script/try_it.sh 1 2 3
Terminal1> ps
Number of bash
s: 3; command given to sub-shell: /path/to/script/try_it.sh
Once again, $0
is the command given to the sub-shell.
My thoughts as to the answer to your question
You asked,
Is there a way to find out exactly what text the user typed for the command that started the script file?
I think that the answer is no. If you go through the steps I've shown, I hope you'll understand a little bit of why you got the behavior you did. I think that, in most cases, you can find out what the user typed. However, I see no way to differentiate between these two commands (once /path/to/script
has been added to your PATH
)
Terminal2> try_it.sh 1 2 3
and
Terminal2> /path/to/script/try_it.sh 1 2 3
Maybe you can figure out a way to tell the difference. If so, post it as the answer.
Note: See this SO post and this link (search "Substring Removal") to get explanations about the ${#%/*}
and ${##*/}
. Thanks to those people for making it so I don't have to write the explanation.
Just for fun
Let's see if I actually programmed in the correct behavior for when someone does use the -s
flag.
$ /path/to/script/try_it.sh -s 1 2 3
Welcome to the program!
Argument 0 is: /home/dblack/other_dir/try_it.sh
Argument 1 is: -s
Argument 2 is: 1
Argument 3 is: 2
Argument 4 is: 3
Working directory: /home/dblack/other_dir
$0 = /home/dblack/other_dir/try_it.sh
Everything before the script name and last / : ${0%/*} = /home/dblack/other_dir
Just the script name: ${0##*/} = try_it.sh
Hit Ctrl+C to stop this program's execution.
It worked. Hooray!