0

Here are the details of my setup. I have the following files:

config.json

{
    "paths": ["/Users/First Folder", "/Users/Second Folder"]
}

test.sh

#!/bin/zsh
monitored_paths=$(jq -r '.paths[]' config.json)
fswatch --verbose "${monitored_paths[@]}"

The paths key in the JSON array needs to be processed by jq and then expanded as arguments. However, when executing test.sh, I encounter this output:

Output:

start_monitor: Adding path: /Users/First Folder
/Users/Second Folder

My expectation is to have:

start_monitor: Adding path: /Users/First Folder
start_monitor: Adding path: /Users/Second Folder

In summary, I aim to monitor two files, but it seems only one file is being monitored. How can I resolve this issue?

EDIT:

Used exact output by request in the comments.

F. Zer
  • 1,081
  • 7
  • 9
  • `monitored_paths` is not an array, it's a string. – Barmar Aug 16 '23 at 19:08
  • See https://stackoverflow.com/questions/35005893/convert-a-json-array-to-a-bash-array-of-strings for how to convert a JSON array to a bash array. – Barmar Aug 16 '23 at 19:10
  • Thank you. I found out this works: `monitored_paths=("${(@f)$(jq -r '.monitoredPaths[]' config.json)}")` to read a JSON array as a bash array. – F. Zer Aug 16 '23 at 19:35
  • 1
    Are you using zsh ? – Philippe Aug 16 '23 at 19:39
  • 3
    Why did you tag this `bash` when you're using `#!/bin/zsh`? I just spent several minutes searching the Bash Manual for `(@f)`. – Barmar Aug 16 '23 at 19:50
  • Sorry, @Barmar. I found that after posting my question, and didn't know that feature wasn't available in `bash`. Will remember that in the future. – F. Zer Aug 16 '23 at 20:16
  • The two shells are very different. You can't say you're using one when you're actually using the other and expect useful answers. – Shawn Aug 16 '23 at 20:24
  • Well @Shawn, that's part of the learning process. If I make a mistake in the design of the question, it's good to be correct. I didn't know every detail, that's why I am seeking for help since there are knowledgeable people here willing to help. – F. Zer Aug 16 '23 at 20:46

2 Answers2

2

NOTE: Following answers were based on the bash tag that was originally attributed to the question; the tag has since been changed to zsh; I don't use zsh but from comments by OP:

  • the while/read loop works in zsh
  • the mapfile does not work in zsh

As mentioned in comments the current monitored_paths assignment populates the variable with a single (2-line) string:

$ typeset -p monitored_paths
declare -a monitored_paths=([0]=$'/Users/First Folder\n/Users/Second Folder')

Notice the embedded linefeed (\n) between the 2 paths.

This entire 2-line construct is fed to fswatch as a single path.

A couple options for populating monitored_paths as an array:

while/read loop:

monitored_paths=()

while read -r path
do
    monitored_paths+=("$path")
done < <(jq -r '.paths[]' config.json)

mapfile:

mapfile -t monitored_paths < <(jq -r '.paths[]' config.json)

Both of these create/populate the array:

$ typeset -p monitored_paths
declare -a monitored_paths=([0]="/Users/First Folder" [1]="/Users/Second Folder")

From here OP's current code should function as expected:

fswatch --verbose "${monitored_paths[@]}"
markp-fuso
  • 28,790
  • 4
  • 16
  • 36
  • It seems "Philippe" is right. `fswatch` inform `start_monitor: Adding path: '/Users/First Folder'`. The single quotes shouldn't be there since the single quotes aren't part of the path. – F. Zer Aug 16 '23 at 20:18
  • I will do that in a second. – F. Zer Aug 16 '23 at 20:22
  • ok, I think I understand where you're coming from ... the `typeset -p` shows `[0]="'/Users/First Folder'"` (single and double quotes) when it should show `[0]="/Users/First Folder"` (only double quotes); the single quotes are a result of OP's `jq -r '.paths[] | @sh' config.json` so, how do you get `jq` to 'remove' both the double quotes (-4) and single quotes? – markp-fuso Aug 16 '23 at 20:38
  • Thank you for your question and answer ! I edited my question. Regarding your concerns, I had to remove `@sh` filter (safely escapes each of the elements in the array. – F. Zer Aug 16 '23 at 20:42
  • I just checked and if we leave the quotes, as guessed, the monitoring process doesn't occur because those single quoted paths don't exist. – F. Zer Aug 16 '23 at 20:43
  • 1
    @F.Zer is right. Removing `| @sh` is enough, assuming there are no special characters (like newlines) in the paths. – Philippe Aug 16 '23 at 20:45
  • ok, that's even simpler, I've update the answer with the `| @sh` removed – markp-fuso Aug 16 '23 at 20:45
  • I upvoted, thank you for your answer. In my system, there is no `mapfile` command, so I can't test. – F. Zer Aug 16 '23 at 20:48
  • @F.Zer there are actually 2 answers ... did the `while/read` loop work? (I know the `while/read` loop works in `bash` but I'm not a `zsher`) – markp-fuso Aug 16 '23 at 20:50
  • Sorry, I didn't see the first using `while/read`. Will test now. – F. Zer Aug 16 '23 at 20:52
  • 1
    You did it ! Congratulations. Now, two paths are passed to `fswatch` – F. Zer Aug 16 '23 at 20:52
  • Finally!!!! :-) – markp-fuso Aug 16 '23 at 20:54
  • 1
    Yes ! Hahaha. Oh, and I can confirm that `while/read` solution works using `zsh`. – F. Zer Aug 16 '23 at 20:54
  • You could add `#!/bin/zsh` to your `while/read` solution to make that clear for future readers. – F. Zer Aug 16 '23 at 20:55
  • I presume you are using `bash 3.2` of MacOS. You can install newer bash version with `brew install bash` which should install bash in `/usr/local/bin/bash` – Philippe Aug 16 '23 at 20:58
  • Yes, that's right @Philippe. I am using `bash 3.2`. Thank you – F. Zer Aug 16 '23 at 21:00
1

in shell syntax from shebang of your example

#!/bin/zsh
typeset -a monitored_paths
jq -r '.paths[]' config.json | while read -r p; do monitored_paths+="$p"; done
fswatch --verbose $monitored_paths
yvs2014
  • 303
  • 2
  • 7
  • Thank you for your answer, but that passes a single path to `fswatch`, instead of two. – F. Zer Aug 16 '23 at 20:49
  • I've checked it with config from your example, it works in linux at least. To debug: firstly run `jq -r '.paths[]' config.json`, if it shows two lines (one per path), then run `zsh -x test.sh`. Suppose it will give some hints – yvs2014 Aug 16 '23 at 20:57
  • You are right, upvoted. Sorry about that. Not sure why (out of habit) added quotes around "$monitored_paths". Do you know why that failed ? – F. Zer Aug 16 '23 at 20:59
  • I mean, I called `fswatch --verbose "$monitored_paths"` like that. – F. Zer Aug 16 '23 at 21:01
  • Sorry I didn't notice `zsh` – Philippe Aug 16 '23 at 21:07
  • @F.Zer "added quotes around "$monitored_paths". Do you know why that failed?" - in this case you got a combined line of args. With double quotes just use `"${monitored_paths[@]}"`, but `$monitored_paths` is shortly – yvs2014 Aug 16 '23 at 21:08
  • 1
    @F.Zer : The quotes are usually not relevant, since zsh does word splitting before parameter expansion. But if you use for an array the syntax with `[@]`, i.e. `"{arrayname[@]}"`, the quotes matter. – user1934428 Aug 17 '23 at 10:14