I'm trying to write a ZSH script so I can run an arbitrary script (say, a JavaScript app) and open the script's output as a file in another application (my IDE). Here, my thinking is to invoke it ./myscript.sh $(node main.js). Content of myscript.sh right now:

TEMPFILE=$(mktemp /tmp/atom.XXXX)
echo $1 > $TEMPFILE
ide --file=$TEMPFILE

I'm running into trouble on the echo line. The application's output is markup, so it has a variety of whitespace, backticks, (single|double) quotation marks, alphanumeric char, symbols, and newlines in it.

I understand the issues have to do with the backticks and quotation mark species in the app's output. Some dummy output:

# Weightlifting Workout A

Bodyweight: 165

| Lift | ||| 1RM (lb) |
|---| ---:|---:|---:| :-- |
|**Squats**|100x5<br>*0.61xBW*|90x6<br>*0.55xBW*|80x8<br>*0.48xBW*|112.5 |
|**Deadlift**|250x2<br>*1.52xBW*|||257.1 |

I imagine the fix is that I need to wrap something around the $1, but not sure what. The newlines in the JavaScript output is messing with echo.

I've tried combinations of ./myscript.sh $(node main.js), ./myscript.sh "node main.js", ./myscript.sh "$(node main.js)"

with echo $1 > file, echo $($1) > file, etc.

playing around with passing the app's output as the zsh argument or passing the app's name as the zsh argument and then the zsh script executing the app's name.

Can't figure out how to get this working without running into an error like "command not found: node main.js" or "command not found: #" or something like that.

2 Answers 2


You can make it:

#! /bin/zsh
(){ide --file=$1} =(print -r -- "$@")

And invoke it as:

that-script "$(cmd)"

The quotes are needed as otherwise the shell would split the output of $cmd on $IFS characters. That's only for NL, SPC, TAB and NUL (the default value of $IFS), quoting characters, backtics, etc are not a problem.

But that's more typing than doing directly:

ide --file =(cmd)

Or if it has to be ide --file=file instead of ide --file file:

(){ide --file=$1} =(cmd)

=(cmd) is a zsh-specific form of process substitution that uses a temporary file instead of a pipe (for <(...)). (){code} args is an anonymous function.

Note that to output the content of $1 followed by a newline, the syntax is either:

printf '%s\n' "$1" # POSIX
print -r -- "$1" # ksh/zsh
echo -E - $1 # zsh only

Not echo $1 which would not work properly if $1 contained backslashes or was something like -n, -, -Enenne... See Why is printf better than echo? for details.


Your script, modified:

TEMPFILE=$(mktemp /tmp/atom.XXXX)
cat >"$TEMPFILE"
ide --file="$TEMPFILE"

You would use this as

node main.js | ./myscript.sh

This passes the output of the node command to the standard input of your script. The script then simply redirects its standard input to the temporary file.


TEMPFILE=$(mktemp /tmp/atom.XXXX)
printf '%s\n' "$1" >"$TEMPFILE"
ide --file="$TEMPFILE"

and use the script as

./myscript "$( node main.js )"

(note the double quoting and the use of printf)

I know I switched to /bin/sh in the above examples, but you don't seem to use any particularly advanced zsh feature, so I thought that would be okay.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.