4

I would like to declare a few shortcut commands to switch between my various coding projects, so I have come up with the following script.

projects=$(ls -d ~/Workspace/*/)
prefix="/Users/myuser/Workspace/"
for f in $projects
do
        temp=${f#$prefix}
        temp=${temp/%\//}
        c="alias $temp='cd $f'"
        echo $c
        eval $c
done

I put that in a file called .workspace-shotcuts.sh in my homefolder, then I chmod +X .workspace-shotcuts.sh it. when I run it, using ./.workspace-shotcuts.sh I get

alias project1='cd /Users/myuser/Workspace/project1/'
alias project2='cd /Users/myuser/Workspace/project2/'

in the console. But the aliases are not declare (zsh: command not found: project1).

Also at the end of my .zshrc file I have added /bin/sh .workspace-shotcuts.sh, which gives the same output, but still no aliases.

Any body can tell me what I am missing? I would like to point out that I am not a great *nix user, so you might have to ELI5 some things to me.

EDIT:

I was suggested to use source instead, which I have done, but here is the error messages I get:

(eval):2: permission denied: /Users/myuser/Workspace/project1/
(eval):3: permission denied: /Users/myuser/Workspace/project2/    
(eval):4: permission denied: /Users/myuser/Workspace/project3/
(eval):5: permission denied: /Users/myuser/Workspace/project4/
(eval):6: permission denied: /Users/myuser/Workspace/project5/
(eval):7: permission denied: /Users/myuser/Workspace/project6/
(eval):8: permission denied: /Users/myuser/Workspace/project7/
(eval):9: no such file or directory: /Users/myuser/Workspace/project1=cd /Users/myuser/Workspace/project1/\n/Users/myuser/Workspace/project2/\n/Users/myuser/Workspace/project3/\n/Users/myuser/Workspace/project4/\n/Users/myuser/Workspace/project5/\n/Users/myuser/Workspace/project6/\n/Users/myuser/Workspace/project7/

What is happening? Is there some special permission I need to give my script?

le-doude
  • 143
  • I think the problem may be that variables set or exported in a script cannot alter the calling (parent) environment. See http://stackoverflow.com/questions/8604462/setting-environment-variable-in-shell-script-does-not-make-it-visible-to-the-she for example. – msw Dec 13 '15 at 02:56
  • @Michael Homer, @cuonglm, @msw ... I have tried source, I do not think it fixed much. – le-doude Dec 13 '15 at 06:07
  • I don't know zsh, but it looks like you're having a problem with a difference between zsh and /bin/sh.  Try running zsh .workspace-shotcuts.sh; it will probably fail the same as the source command.  Then look at zsh documentation to figure out what you need to change to get it to work in zsh.  Then source .workspace-shotcuts.sh should work. – G-Man Says 'Reinstate Monica' Dec 13 '15 at 08:13

1 Answers1

8

One of your problems is that you executed your snippet in a separate shell process, which has no effect on the parent shell. It's the same problem as in How can I make environment variables "exported" in a shell script stick around?. You need to use the source builtin (also avilable under the name .) to execute the script inside the same shell.

source ~/.workspace-shotcuts.sh

Another problem is that you're trying to parse the output of ls. Don't do that. In shells such as sh and bash, you can get away with it because writing $projects outside of quotes splits the value at the newlines that separates the file names. Except that it doesn't actually work: for example, if the file names contain spaces, they will be broken into space-separated pieces. In a shell script, don't parse the output of ls, use wildcards instead. This is rather straightforward in zsh.

projects=(~/Workspace/*/)
for f in $projects; do …

What you're doing next is rather convoluted. You don't need eval here; using it only sets you up for quoting mishaps. Since you're using zsh, you can use the history modifier t to extract the last component of a path, without reaching for string manipulation constructs: $f:t. In case the file name contains special characters, you should protect them, and again zsh makes this easy thanks to the parameter expansion flag q: ${(q)f} gives you a quoted filename that you can use in the alias definition.

projects=(~/Workspace/*/)
for f in $projects
do
  alias $f:t="cd ${(q)f}"
done

But in fact, you're just reinventing cdpath plus auto_cd. Scratch all that and use

setopt auto_cd
cdpath+=~/Workspace

The auto_cd option lets you type a directory name to change into it, without having to type cd ‍ before it. The cdpath is an array of directory prefixes that cd foo tries if there isn't a subdirectory called foo in the current directory.


In case bash users see this thread: the alias method would be

projects=(~/Workspace/*/)
for f in "${projects[@]}"; do
  f=${f%/}
  alias ${f##*/}="cd '${f//\'/\'\\\'\'}'"
done

and the CDPATH method would be

CDPATH=$CDPATH:$HOME/Workspace
shopt -s autocd