I have a file that have shell commands
For eg: test
I need to run this as
#test
instead of #./test
Anyone please help!
Commands that don't have / in them are tried as
shell builtins
functions
aliases
executables in each component of $PATH
PATH is an environment variable that contains a colon-separated list of paths where the shell or or some of the exec system functions search for executable files.
PATH=$PWD:$PATH
Prepends the current directory, making your test executable runnable.
(As hek2mgl mentions, appending is normally much better for security reasons as you normally don't want user-writable paths to override write-protected paths that are already part of PATH--though you do want it in this case because test is already a system-level executable)
It will still be preceded by the test shell builtin, however.
For that reason, you should name it something other than test, or circumvent the builtin with $(which test) after you modify your PATH.
In a shell, you could also make it a function (or an alias ) that would invoke the full path. That wouldn't have many bad security implications.
Note:
$PWD is the absolute path to your current directory. You could also use . (or another relative path), making the PATH "change" as you cd along, however that is considered a bad practice for security reasons.
Related
Consider the following folder structure:
$ tree ~/test_path
test_path
`-- sub_folder
`-- script.sh
1 directory, 1 file
Let's say that you have added test_path to your path by
export PATH=$PATH:~/test_path
$ whereis sub_folder
sub_folder: /home/murtraja/test_path/sub_folder
Now how to execute script.sh by calling sub_folder/script.sh?
$ sub_folder/script.sh
bash: sub_folder/script.sh: No such file or directory
EDIT: I don't want to change the call sub_folder/script.sh because this is called by another script which I cannot (am avoiding to) change.
Short answer: You can't, but depending on the set of constraints you're facing, there might be another way to handle it.
Long answer: When a command name contains at least one slash character, it treated as a path to the executable (i.e. it doesn't search the directories in $PATH). With the command name sub_folder/script.sh, it contains a slash, but doesn't start with a slash, so it'll be resolved relative to the current working directory.
So there are a couple of possibilities for making this work:
If you can cd to ~/test_path before running this, it'll find it directly. Of course, this may break other things (i.e. anything else that uses relative paths and/or plain filenames and expects them to be resolved somewhere else). Also, be sure to check for errors when you cd, or the script could execute in an unexpected directory, with unexpected consequences.
If the script needs to execute from a different working directory, you might be able to create a symbolic link from sub_folder in that working directory to ~/test_path/sub_folder. But depending on where the script's working directory is, this may be impossible or unsafe. I'd avoid using this option if possible.
There's also an option that depends on a weird/nonstandard feature of bash: the ability to define function names with slash in them. But this has weird limitations depending on the version of bash you have:
You can define a function like this:
sub_folder/script.sh() { ~/test_path/sub_folder/script.sh "$#"; }
and then either use export -f sub_folder/script.sh (so bash subprocesses inherit it), or do this in a wrapper script and then source the script you can't change from there (so it's the same shell, and inheritance isn't necessary).
Difficulty: some versions of bash refuse to export functions with weird names, and some refuse to inherit them. So the export method might or might not work (and might break unexpectedly due to an update). The source method might be better, but also might cause other trouble.
If there's any way at all to change the other script, that'd really be the best option.
Since you've added it to your path, you can just call the script by name. It should also let you tab complete as well.
$ ./script.sh
Out of curiosity I called which myscript.sh for a script I have under ~/bin/myscript.sh (in macOS's bash). I can execute the script with no problems from any directory without giving its full path. ~/bin is on my PATH and the script has executable flags set, ls -l outputs -rwxr-xr-x.
I would have expected which to show me the script's full path, but it didn't output anything.
Is this intended behaviour or is something odd happening here?
This happens when you add a literal tilde ~ to your PATH, instead of the actual path to your home directory. Rewriting ~ into /home/youruser is the job of the shell, not of the tool or filesystem. If you e.g. quote the "~", this rewrite doesn't happen and other tools like which get confused.
Here's information on this issue from the shellcheck wiki:
Literal tilde in PATH works poorly across programs.
Problematic code:
PATH="$PATH:~/bin"
Correct code:
PATH="$PATH:$HOME/bin"
Rationale:
Having literal ~ in PATH is a bad idea. Bash handles it, but nothing else does.
This means that even if you're always using Bash, you should avoid it because any invoked program that relies on PATH will effectively ignore those entries.
For example, make may say foo: Command not found even though foo works fine from the shell and Make and Bash both use the same PATH. You'll get similar messages from any non-bash scripts invoked, and whereis will come up empty.
Use $HOME or full path instead.
For a long time I was under impression that *NIX shells do not support running executables via path relative to current directory. Also there're a number of posts explaining why having working directory in path is a bad practice. Still it's easy to see that following example works:
% cat<<EOF > temp/a
? #!/bin/sh
? echo "Hello looser!"
? EOF
% chmod 750 temp/a
% temp/a
Hello looser!
(I tested at CentOS and OSX).
Why it works?
Upd: it's not purely academic question, I had cases when binaries run via full path and the way described above worked different way.
Upd 2: libc execv accepts both absolute and relative paths with no problem. Hence, support for subpath/exe while failing to run exe looks as shell feature common to bash and tcsh. There must be some logic behind it??
You can always execute a program by specifying its relative path. There is no requirement that you would have to use an absolute path. Actually, the way you are executing your program, is quite common.
If you write command with no / then the shell searches $PATH for the command.
If you write dir/command with a relative directory, there's a / and the shell does not search $PATH. It interprets it relative to the current directory. dir/command always resolves to ./dir/command.
Putting . in the $PATH is dangerous because you may type command intend ing to run /usr/bin/command but actually get ./command if it exists in the current directory. Indeed, it's not just . that's dangerous: any relative path is dangerous. $PATH should only ever contain absolute paths.
So, you're saying that relative paths shall be implicitly prefixed with current directory, with exceptional case when relative path is empty that shall be treated as direction to search in $PATH?
Yes, you could say that. But I prefer to reframe it. A better way to think of it is to understand what the shell is doing versus what the kernel is doing.
Interpreting relative paths is handled implicitly by the kernel. When the shell performs a system call like execve("dir/command", ...) to execute a program the kernel knows the parent process's current directory and resolves dir/command relative to it. The shell does not have to first convert the path into an absolute one. The kernel can handle both absolute and relative paths.
The kernel knows nothing about $PATH, though. That's a shell construct. Keep in mind that the shell is a higher level piece of software. The shell decides that if you type a simple command name without a / in it, it will not simply pass that command to the kernel. If it did, typing cat would simply execute ./cat. Nobody wants that.
Instead, the shell decides that the lack of a / means it will search for the command in the $PATH. It'll search /bin, /usr/bin, $HOME/bin, etc. If you listed . in the $PATH then it would also search the current directory—don't do that! You don't want it to run ./cat. No sir.
If the shell finds an executable in the $PATH then it converts it to an absolute path name and passes that to the kernel. The kernel sees execve("/usr/bin/cat", ...).
If so and you know it from some formal specification, I'd appreciate the reference. I do need to know precisely.
See the bash man page:
If the command name contains no slashes, the shell attempts to locate it. …
If the name … contains no slashes, bash searches each element of the PATH for a directory containing an executable file by that name. …
If the search is successful, or if the command name contains one or more slashes, the shell executes the named program ….
This as nothing to do with execution. It is due to the fact that you can name a file with an absolute or a relative name, and that executables are just ordinary files.
Why is it dangerous? (.in PATH)
Think about a script that calls a command like cat foo, if you have . in the path then it could be possible to create a command cat in the same directory that would be executed in place of the original one.
This is the error that I got.
modpath: Command not found.
I'm suspecting that it's because modpath is a csh command but not the right syntax for bash. So, I want to know what's its equivalent in bash. Thanks!
modpath - change global search path for dynamically loadable kernel modules
modpath allows users with appropriate privilege to modify the global search path used to locate object files for dynamically loadable kernel modules. The search path modifications take effect immediately and affect all subsequent loads for all users on the system.
pathname may be either a colon-separated list of absolute pathnames or NULL. If the former, these path names represent directories which should be searched for all autoloads of loadable kernel modules and for demand loads (see modload(2)) where the module is given by a simple file name. This list of directories will be prepended to the existing list of directories and so will be searched before any directories given in previous calls to modpath and before the default location which is always searched last. The directories do not have to exist on the system at the time modpath is called, or when a load actually takes place. If pathname is equal to NULL, the global search path is set back to its initial default value, /stand/dlkm/mod.d.
Notes: modpath is currently implemented as a macro.
[source: http://modman.unixdev.net/?sektion=2&page=modpath&manpath=HP-UX-11.11]
Based on the documentation now included in the question, modpath has nothing to do with $PATH, and it's not specific to csh, bash, or any other shell.
On my system (Ubuntu 16.10), there is no command, system call, or library function by that name, and there appears to be no installable package that provides it. The documentation you quoted is for HP-UX, which is a different flavor of UNIX. I suspect it's a system call that exists only on HP-UX.
Since you've been able to invoke modpath from csh, there's probably a command that's some kind of wrapper around the system call, though the documentation is for the system call itself, not for the command.
If such a command exists, you should be able to find it by typing
which modpath
from csh. If that gives you the full path to the command, then from bash you can either use that full path to invoke the command, or you can add the appropriate directory to your $PATH. (You very likely have a different setting for $PATH in csh vs. bash.)
The following fails with Errno::ENOENT: No such file or directory, even if the file exists:
open('~/some_file')
However, I can do this:
open(File.expand_path('~/some_file'))
I have two questions:
Why doesn't open process the tilde as pointing to the home directory?
Is there a slicker way than File.expand_path?
Not sure if this was available before Ruby 1.9.3 but I find that the most elegant solution is to use Dir.home which is part of core.
open("#{Dir.home}/some_file")
The shell (bash, zsh, etc) is responsible for wildcard expansion, so in your first example there's no shell, hence no expansion. Using the tilde to point to $HOME is a mere convention; indeed, if you look at the documentation for File.expand_path, it correctly interprets the tilde, but it's a feature of the function itself, not something inherent to the underlying system; also, File.expand_path requires the $HOME environment variable to be correctly set. Which bring us to the possible alternative...
Try this:
open(ENV['HOME']+'/some_file')
I hope it's slick enough. I personally think using an environment variable is semantically clearer than using expand_path.
Instead of relying on the $HOME environment variable being set correctly, which could be a hassle when you use shared network computers for development, you could get this from Ruby using:
require 'etc'
open ("#{Etc.getpwuid.dir}/some_file")
I believe this identifies the current logged-in user and gets their home directory rather than relying on the global $HOME environment variable being set. This is an alternative solution to the above I reckon.
I discovered the tilde problem, and a patch was created to add absolute_path
which treats tilde as an ordinary character.
From the File documentation:
absolute_path(file_name [, dir_string] ) → abs_file_name
Converts a pathname to an absolute pathname. Relative paths are referenced from the current working directory of the process unless dir_string is given, in which case it will be used as the starting point. If the given pathname starts with a “~” it is NOT expanded, it is treated as a normal directory name.