I typed Example 2-3 from Cooper`s Advanced Bash-Scripting Guide into my ~/bin
, and it will only run if I include ./
before the filename. My other scripts in the same directory do not behave this way. $PATH
includes ~/bin
. I copied the file into /usr/local/bin
, and it runs without ./
.
./
is needed to run a script not contained in the PATH variable and located in the current directory.
look into the PATH-Variable and check if the directory of the script is really contained in it. Be aware to check for typos! This applys to all scripts in the same directory.
Some more info would be beneficial. For example:
- Is the script really located in
~/bin
? - Any error messages?
- Are the permissions set correctly?
- Does the script refuse to start, or does it fail somewhere in the middle? The
-x
(xtrace) option might help. - Does it use a special interpreter / shell, not your default one?
- Do you have the "shebang" set?
- How does it differ from the scripts that do run?
Just to name a few...
Expanding a little bit on what stomp and RudiC have already said...
What modes are displayed for the files in ~/bin
that run without ./
when you run the command:
ls -l ~/bin
What modes are displayed for the files in ~/bin
that do not run without ./
?
What other directories are in $PATH
before ~/bin
?
Do any of those directories contain a file with the same name as one of the files you are trying to execute?
Normally a shell will execute the first executable file found while going through the directories listed in $PATH
in the order in which they appear in $PATH
. Some shells will remember where they found an executable file on a previous search and won't look again unless a pathname that had been remembered stops working.
If an execute permission bit that allows you permission to execute it isn't set on a file that you're trying to execute, you usually won't be permitted to execute it by trying to run filename
or by ./filename
, but the command pathname_of_shell filename
will work if filename is a valid script in the shell language recognized by the shell you're invoking with pathname_of_shell
.
@stomp--I double-checked. The file in question is Cleanup2.
echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/home/m*****/bin
~/bin$ ls
case4 counter foo lw2 posit-params3
cleanup2 file_info longest-word old_sys_info_page sys_info_page
cleanup3 file-info lw old_sys_info_page.html sys_info_page.html
--- Post updated at 04:15 PM ---
@RudiC and @Don
the file is located in ~/bin
(see above)
error message
~/bin$ sudo cleanup2
sudo: cleanup2: command not found
permissions
~/bin$ ls -l cleanup2
-rwxr-xr-x 1 m***** m***** 459 Jan 28 15:07 cleanup2
~/bin$ ls -l cleanup3
-rwxr-xr-x 1 m***** m***** 2057 Jan 29 06:13 cleanup3
shebang
#!/bin/bash
# Proper header for a Bash script
As far as differences, go, I can't find any. Cleanup3 runs fine, but Cleanup2 won't.
Don, I tried locate cleanup
and found some other files containing that string, but they aren't in $PATH
Note that cleanup2
and cleanup3
are not the same as Cleanup2
and Cleanup3
; case matters in the shell command language!
What output do you get from the commands:
od -bc ~/bin/cleanup2 | head -n 10
od -bc ~/bin/cleanup3 | head -n 10
You didn't mention you're using sudo
. The effective user (by default) is root
, then. root
's $PATH
does NOT have ~/bin
appended, or, if it has, it won't point to YOUR $HOME
nad thus doesn't find cleanup2
.
Does sudo cleanup3
run correctly?
Please make sure to post such important info in the first place in future!
To add to already quoted:
<CR> = The Enter/Return key " |
<-' "
./executable[.extension]<CR>
^^ ^
|| |
|| +---> The filename to run and MUST be set to executable, using 'chmod' if necessary.
|+-------------> The current directory/drawer/folder path seperator.
+--------------> The __pointer__ to the current directory/drawer/folder.
OR...
/absolute/path/to/your/executable[.extension]<CR>
OR as in your case:
executable[.extension]<CR>
From any directory/drawer/folder that is inside the $PATH environment variable.
EDIT:
Even these will work from your current path, but not recommended:
././executable[.extension]<CR>
AND
./././executable[.extension]<CR>
I can only emphasize on what RudiC already said: running sudo command
is a completely different thing than running command
. Basically sudo
does:
1) switch to another user acount (by default this is root)
2) execute command
as this user
3) switch back.
Of course, a different user can have (and most probably has) a different environment and when you change yours then his is not affected at all. It does not matter what is in your PATH, what matters is what is in roots PATH.
Try sudo echo $PATH
and most probably /your/home/bin
will not be included in it. If you can change it you could do that although i think it would be a bad idea.
Instead, put a script that should be run by several users (and eventually as root) in /usr/local/bin
or in either /usr/local/sbin
or /root/bin
if it should only be run as root. Put /root/bin
or /usr/local/sbin
in roots PATH in this case (i suppose /usr/local/bin
to be already included there). Then you can use the command you used.
I hope this helps.
bakunin
PS: you probably wonder why it works with ./command
. The reason is that the "." for "current directory" is expanded by the shell even before any attempt of executing any command is made. Therefore the shell replaces "." with something like "/path/to/your/home/bin" and only then executes sudo /path/to/your/home/bin/cleanup2
so that the user you switch to doesn't have to have it in its PATH to find it.
bakunin
To all: thanks for your input!
Today I learned: $PATH is not the same for myself and root.
@Don--I typed Cleanup in the post but I wasn't doing so in the script.
Here is the output of those commands:
~$ od -bc ~/bin/cleanup2 | head -n 10
0000000 043 041 057 142 151 156 057 142 141 163 150 012 012 012 043 040
# ! / b i n / b a s h \n \n \n #
0000020 120 162 157 160 145 162 040 150 145 141 144 145 162 040 146 157
P r o p e r h e a d e r f o
0000040 162 040 141 040 102 141 163 150 040 163 143 162 151 160 164 012
r a B a s h s c r i p t \n
0000060 043 040 143 154 145 141 156 165 160 062 072 040 151 155 160 162
# c l e a n u p 2 : i m p r
0000100 157 166 145 144 012 043 040 122 165 156 040 141 163 040 162 157
o v e d \n # R u n a s r o
------------------------------------------------------------------------
~$ od -bc ~/bin/cleanup3 | head -n 10
0000000 043 041 057 142 151 156 057 142 141 163 150 012 012 012 043 040
# ! / b i n / b a s h \n \n \n #
0000020 143 154 145 141 156 165 160 063 012 043 040 040 127 141 162 156
c l e a n u p 3 \n # W a r n
0000040 151 156 147 072 012 043 040 040 055 055 055 055 055 055 055 012
i n g : \n # - - - - - - - \n
0000060 043 040 040 124 150 151 163 040 163 143 162 151 160 164 040 165
# T h i s s c r i p t u
0000100 163 145 163 040 161 165 151 164 145 040 141 040 156 165 155 142
s e s q u i t e a n u m b
--- Post updated at 07:52 PM ---
@ RudiC and @bakunin
Thanks to your guidance, I strongly suspect the problem is not having my home directory is root's $PATH
.
Cleanup3 was only running because I had placed a copy of it in /usr/local/bin
.
Now I just need to figure out how to write bakunin's script!
You presented the solution already. Why don't you put cleanup2
into /usr/local/bin
as well?
Good point, EXCEPT, if you look at the hex-dump of cleanup2
it quotes, "Run as ro[ot]"...
0000100 157 166 145 144 012 043 040 122 165 156 040 141 163 040 162 157
o v e d \n # R u n a s r o
So there must be a reason why it needs to be run as root.
Just an observation.
Chances are that root
does run scripts in /usr/local/bin
as proven by
Hi RudiC...
Yes but Cleanup3 does not run as root and could be anywhere.
However maybe Cleanup2 has to be in more secure location so that any 'Tom, Dick or Harry' can't execute it unless they have root access.
Maybe it is 'critical', term used loosely, to the correct 'cleanup' method. It would be interesting to know the access rights of Cleanup2.
This was my point, perhaps I am being too pedantic here...
(Anyhow, I am rigged for silent running on this thread now.)
Bazza.
Well, wisecaracker, I see your point, but - IF a script does such delicate things that needs root privileges, it certainly should NOT reside in a user's $HOME/bin
. And, sudo is so flexible to setup that only the eligible user can use cleanup2
in a public directory.
Except that your shell will evaluate $PATH
before executing sudo
. Instead you would need to do the somewhat iffy looking
sudo bash -c "eval echo \$PATH"
or better still get use sudo -i
and check your path.
Agreed. So place the program in /usr/local/sbin
, which on my system at least, is in root's path when using sudo
. And make it executable (and readable?) only by root.
Andrew
I appreciate the help; I've learned a lot!
Here`s the original script, which I did not write.
#!/bin/bash
# Proper header for a Bash script
# cleanup2: improved
# Run as root, of course.
# Insert code here to print error message and exit if not root.
LOG_DIR=/var/log
# Variables are better that hard-coded values.
cd $LOG_DIR
cat /dev/null > messages
cat /dev/null > wtmp
echo "Logs cleaned up."
exit # The right and proper method of "exiting" from a script.
# A bare "exit" (no parameter) returns the exit status
#+ of the preceding command.
IS THAT IT, (and needs to be run from root)?
Why use:
cat /dev/null > messages
cat /dev/null > wtmp
When:
: > messages
: > wtmp
......is much simpler.
Useless use of cat?
No disrespect to you Xubuntu56, you are innocent of this one...
BUT THAT........
........guys was the improved version, what was the original like I wonder!
Hopefully this does NOT attach to MY previous post.
A rewrite of the code to have an optional user path.
Usage: cleanup2 [/optional/full/path/to]
#!/bin/sh
# Usage: cleanup2 [/optional/full/path/to]
# POSIX compliant.
# Due to '/var/log/' this HAS to be run from root.
# No need to detect root as the error generated is enough.
# Attempt to obtain the user path argument.
LOG_DIR="$1"
# Fall back to the default if user path is NOT supplied.
if [ "${LOG_DIR}" = "" ]
then
LOG_DIR="/var/log"
fi
# *****************************
# Not required but commented out so you can try both meathods.
# cd "${LOG_DIR}"
# Clear require files. ":" is a NULL, NOP or true command.
# : > messages
# : > wtmp
# These eight lines can be deleted.
# *****************************
# Create empty files in the required path.
: > "${LOG_DIR}"/messages
: > "${LOG_DIR}"/wtmp
# Print success and exit script.
echo "Success! Logs cleaned up."
exit
Results:
OSX 10.14.1, default bash terminal, code calling 'sh'.
AMIGA:amiga~/Desktop/Code/Shell> chmod 700 cleanup2
AMIGA:amiga~/Desktop/Code/Shell> ls -l /tmp/
total 0
drwx------ 4 root wheel 128 16 Nov 12:34 PKInstallSandbox.MMTz8b
drwx------ 3 amiga wheel 96 30 Jan 18:07 com.apple.launchd.CPHRzeQ4Vr
drwx------ 3 amiga wheel 96 30 Jan 18:07 com.apple.launchd.qgu8OpjEvP
drwxr-xr-x 2 root wheel 64 30 Jan 18:00 powerlog
AMIGA:amiga~/Desktop/Code/Shell> ./cleanup2 /tmp
Success! Logs cleaned up.
AMIGA:amiga~/Desktop/Code/Shell> echo $?
0
AMIGA:amiga~/Desktop/Code/Shell> ls -l /tmp/
total 0
drwx------ 4 root wheel 128 16 Nov 12:34 PKInstallSandbox.MMTz8b
drwx------ 3 amiga wheel 96 30 Jan 18:07 com.apple.launchd.CPHRzeQ4Vr
drwx------ 3 amiga wheel 96 30 Jan 18:07 com.apple.launchd.qgu8OpjEvP
-rw-r--r-- 1 amiga wheel 0 30 Jan 19:44 messages
drwxr-xr-x 2 root wheel 64 30 Jan 18:00 powerlog
-rw-r--r-- 1 amiga wheel 0 30 Jan 19:44 wtmp
AMIGA:amiga~/Desktop/Code/Shell> ./cleanup2
./cleanup2: line 25: /var/log/messages: Permission denied
AMIGA:amiga~/Desktop/Code/Shell> echo $?
1
AMIGA:amiga~/Desktop/Code/Shell> _
Andrew, you are right. My excuse is that my dog ate the single quotes. ;-)) It was meant to be sudo echo '$PATH'
, but you are equally right about sudo -i
.
This observation is correct but the return code being returned will be that of echo
, no? To be honest i have never seen this command reurn anything else than TRUE. I would have a hard time even conceiving a situation where it fails. But you probably want to base the return code not on the success ot the echo
but on the success of cleaning the logs.
It makes sense to base the return code of scripts on the success (or lack of it) of certain critical commands. The way to do that is like this: suppose your script will have - at its core - to execute three commands: command1
, command2
and command3
in that order. Now, each of these commands could fail and you want to be able to find out how your script has failed - has it failed to execute command1? Or command2 or command3? So let us say you want to exit your script with:
0 when it succeeds (that is: all three commands excuted successfully);
1 when excuting command1 failed (the others are skipped in that case);
2 when excuting command2 failed (the last is skipped in that case);
3 when excuting command3 failed;
The logic to implement that is like this:
#! /bin/bash
if ! command1 ; then
exit 1
fi
if ! command2 ; then
exit 2
fi
if ! command3 ; then
exit 3
fi
exit 0
The if
executes any command and branches based on the return code of that command "!" is just a logical NOT and means branch if the command returns a non-zero (=logical FALSE) return code. Note that it doesn't matter exactly how the commands fail - they could have all sorts of error codes. This is why this is only the most basic form of logging, but since you do understand now the principle i am sure you can come up with more elaborate solutions should you need them.
Dear brother of the church of ongoing simplification, you surely noticed by now that you wasted four (!!) perfectly usable keystrokes with your solution too, no? This:
> messages
> wtmp
is even simpler than the much simpler solution. ;-))
I hope this helps.
bakunin