If you’re working with JSON data in shell scripts and need to count occurrences of a specific value (e.g., “pending”) using Bash, you might encounter a surprising syntax error—especially when executing the command remotely over SSH or inside a CI/CD pipeline.
Here’s a real-world example. Suppose you have a JSON string stored in a Bash variable and want to count how many times "pending"
appears inside it.
You might be tempted to write:
echo "${jsonString}" | sed 's/},{/}\n{/g' > output1.txt
numberOfTransactions=$(grep -o 'pending' output1.txt | wc -l)
echo "$numberOfTransactions"
This works locally. However, when you run it remotely like:
ssh user@server "
echo \"${jsonString}\" | sed 's/},{/}\n{/g' > output1.txt
numberOfTransactions=\$(grep -o 'pending' output1.txt | wc -l)
echo \"\$numberOfTransactions\"
"
You may encounter:
bash: -c: line 0: syntax error near unexpected token `('
Why does this happen?
The issue occurs because:
- Parentheses
()
are special characters in Bash. - When wrapped in multiple levels of quotes (SSH, CI/CD), Bash misinterprets unescaped characters.
- The
$(...)
syntax for command substitution inside a double-quoted string must be escaped as\$(...)
.
Without proper escaping, Bash thinks the parenthesis is unexpected.
How to fix it?
To avoid this syntax error, you need to:
- Escape each
$(
as\$(
in the SSH command. - Separate commands with semicolons (
;
) inside the string. - Or—best practice—move the logic to a script file and execute the file remotely.
Here’s a corrected single-line solution:
ssh user@server "
echo \"\${jsonString}\" | sed 's/},{/}\n{/g' > output1.txt;
numberOfTransactions=\$(grep -o 'pending' output1.txt | wc -l);
echo \"\$numberOfTransactions\"
"
Alternatively, to avoid temporary files and use piping only:
ssh user@server "
echo \"\${jsonString}\" | sed 's/},{/}\n{/g' | grep -o 'pending' | wc -l
"
Key takeaways:
Always escape $()
as \$()
in nested quotes.
Use ;
to separate commands inside an SSH string.
Move complex commands to a script file for clarity and safety.
Bonus: Using jq
for JSON parsing
Instead of relying on grep
and sed
, consider using jq
for JSON parsing:
echo "${jsonString}" | jq '[.. | .bookingStatus? | select(. == "pending")] | length'
This method is safer and more accurate.
Conclusion
Handling JSON data inside Bash scripts—especially over remote execution—requires careful quoting and escaping. By following best practices, you can avoid syntax errors and write more maintainable scripts.
For production environments, consider using dedicated JSON tools like jq
to avoid brittle text parsing.