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?

3
  • I think the problem may be that variables set or exported in a script cannot alter the calling (parent) environment. See stackoverflow.com/questions/8604462/… for example.
    – msw
    Commented Dec 13, 2015 at 2:56
  • @Michael Homer, @cuonglm, @msw ... I have tried source, I do not think it fixed much.
    – le-doude
    Commented Dec 13, 2015 at 6: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. Commented Dec 13, 2015 at 8:13

1 Answer 1

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
4
  • 1
    I'd move the cdpath suggestion into a TL;DR line at the top of the post :)
    – chepner
    Commented Dec 14, 2015 at 21:14
  • Also, I think you're missing setopt AUTOCD (for zsh) and shopt -s autocd (for bash) to complete the idea of using a directory name as an alias for changing to that directory.
    – chepner
    Commented Dec 14, 2015 at 21:16
  • @chepner Duh, thanks. After all these years on autocd, I tend to forget that it's one of the things that made me switch to zsh back when bash didn't have it. Commented Dec 14, 2015 at 22:16
  • sorry for the accepted answer arriving late
    – le-doude
    Commented Feb 2, 2020 at 23:45

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .