PeterVandivier
When testing parts of long shell scripts, sometimes I like to copy-paste parts of the script to another shell session and execute them line-by-line.
I recently changed my default shell to `zsh` [at the naggy insistence of my MacBook Pro][1]. Since I did this though, I can't seem to debug for-loops. This sucks.
Given a file at `/tmp/foo.csv` of the form...
```csv
a,b
1,x
3,z
2,y
```
...and a `/tmp/foo.sh` of the form...
```sh
order=`tail -n +2 foo.csv | sort --field-separator=',' --key=1 -n`
for row in $order
do
file=`echo $row | awk -F"," '{print $2}'`
eval "./$file.sh"
done
```
...(and also)...
```sh
cd /tmp
echo 'echo x' > x.sh
echo 'echo y' > y.sh
echo 'echo z' > z.sh
chmod +x *.sh
```
...when I copy-paste the for-loop into a shell session, I get this error!
```sh
➜ /tmp for row in $order
do
file=`echo $row | awk -F"," '{print $2}'`
eval "./$file.sh"
done
zsh: no such file or directory: ./x
zsh: command not found: y
zsh: command not found: z.sh
➜ /tmp
```
What _really sucks_ though is that this behaviour is not repeatable when you execute the file _even without a shebang_.
```sh
➜ /tmp echo $SHELL
/bin/zsh
➜ /tmp ./foo.sh
x
y
z
➜ /tmp
```
You _can_ get similar bad behaviour in the file invocation if you explicitly specify `zsh` in the ./foo.sh shebang.
```sh
➜ /tmp cat foo.sh
#!/usr/bin/env zsh
order=`tail -n +2 foo.csv | sort --field-separator=',' --key=1 -n`
for row in $order
do
file=`echo $row | awk -F"," '{print $2}'`
eval "./$file.sh"
done
➜ /tmp ./foo.sh
(eval):1: no such file or directory: ./x
(eval):2: command not found: y
(eval):3: command not found: z.sh
➜ /tmp
```
So it _appears_ that either `zsh` or some "helpful" setting in [ohmyzsh][2] is using `bash` for file invocation when no shebang is specified except...
```sh
➜ /tmp echo $SHELL
/bin/zsh
➜ /tmp echo 'echo $SHELL' > bar.sh
➜ /tmp chmod +x bar.sh
➜ /tmp ./bar.sh
/bin/zsh
➜
```
So... wtf `zsh`, what gives?
[1]: https://www.theverge.com/2019/6/4/18651872/apple-macos-catalina-zsh-bash-shell-replacement-features
[2]: https://ohmyz.sh/
Top Answer
Skillmon
There's just one tiny bit of change necessary. In Zsh if you want to loop over rows in a variable, the variable must be an array. Arrays are initialized with `()` around them (see [the documentation](http://zsh.sourceforge.net/Doc/Release/Parameters.html#Array-Parameters)).
If you declare `order` like this:
```zsh
order=($(tail -n +2 foo.csv | sort --field-separator=',' --key=1 -n))
```
everything should work (note that I changed the backticks to `$(` and `)` as that's the preferred syntax in Zsh over the easy to miss ticks).
So your `foo.sh` should look like this (replace the shebang with the correct one on your system):
```zsh
#!/usr/bin/zsh
order=($(tail -n +2 foo.csv | sort --field-separator=',' --key=1 -n))
for row in $order
do
file=$(echo $row | awk -F"," '{print $2}')
eval "./$file.sh"
done
```
The other files can stay the same.