142 comments

[ 2.8 ms ] story [ 199 ms ] thread
I really really enjoy using zx.

As a dev it lets me get back to a good scripting language, where I process input, run other programs, emit output. Powerful & succinct.

As a tool maker, it helps me show my work. Many of the common tasks like running an script or calling fetch will by default log what's happening in clear format, pass through the activity. It's fantastic to write these devtools that do a bunch of tasks, but are showing their work at every step. If something goes wrong, the person immediately sees the evidence of what was ran & what the output was. It helps close the iteration loop very effectively.

Zx makes such a huge difference, while being little more than a couple small good ideas to help node.js be more shell-scipty. Good stuff.

I haven't tried Dax, another scripting system atop js, but I see zx glowingly mentioned in it's recent submission. https://news.ycombinator.com/item?id=39315689

An immediately visible problem with zx is that wrapping commands in $`` makes it harder to copy and paste them back and forth to the shell to test them. Did that slowed you down sometimes or added an occasional bug?
This looks really cool, actually, and I'm surprised I haven't heard of this before.

My incorrect gut intuition is that JavaScript devs usually don't do very much in depth terminal level command work, but that's of course false on a Bayesian level. There are far more JavaScript developers in the world than there are even Python developers, to say nothing of the long tail of even less popular languages. If 30% of JS devs are heavy terminal users, and 80% of (say) Perl devs, that 30% will still swamp the 80% in terms of absolute numbers.

Hell, that's half the point of Node. Can't believe my gut isn't keeping up with my head here!

Here's an anecdote to help your gut. I'm a platform engineer who's favorite language is Typescript. I LIVE in the terminal. The only reason I use vscode is cause it's easier to put him into vscode than it is to put vscode into vim. Otherwise I'd be in the terminal for everything but browsing.
Likewise! I use TypeScript everywhere possible, and zx is a godsend. Unfortunately, I've recently dealt with gobs of Python for ML software. After years of writing TypeScript, it's been a...sobering reminder of many painful coding issues that I'd forgotten even existed. Made me deeply appreciate what TS gives us haha.
Would a python developer feel the same in reverse?
If they'd go to JavaScript, they'd feel a sense of unfamiliarity similar to what JS developers going to Python would feel.

But the Python equivalent of TypeScript is mypy, and I don't think it's controversial to say (even among Python developers) that it's way behind TypeScript, generally.

I worked at a Python shop for a bit. I spent more time than I wish to admit trying to make all of my Python behave like TypeScript (adding typings, etc.).
Ditto. Python(3.13) types are so crudimentary that it becomes a Sisyphian struggle. Library type support is patchy at best. **kwargs is an abomination. Python is incredibly slow. Async code and anonymous functions are awkward and verbose. Hell, Python is verbose.[1] I could complain endlessly, but I'm mainly just disappointed in how little has changed since I last professionally wrote Python. The biggest benefits were basically a) familiarity, and b) powerful stats/ML libs.

I hadn't seen analogs of `AttributeError`s and `TypeError`s for years, until revisiting Python and remembering that those are still unsolved issues outside of TypeScript. Ugh.

[1] https://news.ycombinator.com/item?id=36407112

Python now has type hints which makes it closer to typescript, and easier to code in.
> There are far more JavaScript developers in the world than there are even Python developers

I don’t think that’s true. Anecdotally when I was interviewing at a recruiting company (and we let candidates pick any language to be assessed in), Python was by far the most popular choice, chosen by about 60% of candidates or something like that. Java and JavaScript came after, followed by a long tail of languages like C# and go.

I assumed JavaScript would be the most popular too - but that didn't seem to be the case. I have no idea where the demand for all this Python programming is coming from, but it seems Python programmers are everywhere. Maybe it’s just popular in schools.

If you need a programming language for you job, but you're not primarily a developer, and two languages would probably be overkill, then your one language is probably python.

That's a lot of people.

That’s exactly how I chose Python.
I'm curious, what was the job that needed some code but not a dedicated developer?

I'm taking computational biology courses on the side, so that's the application that comes to mind for me.

I do digital marketing for clients. There’s a large number of activities that can be made more efficient, quicker, cheaper, or better using code.

For example taking a large number of text documents and turning them into video outlines with illustrations using OpenAI APIs and a quick and dirty Django app to organise everything.

Or connecting up various marketing APIs using Python instead of Zapier - a lot of companies spend thousands of dollars on Zapier each month when a script would literally cost a dollar to run.

Python is just fastest to write. No semicolons, fewer parens, no braces, no const / var / let. List / dict / set comprehensions. Lambdas are clunkier, but `def` is shorter than `function`. Etc.

Perl and Ruby are comparably compact and expressive, but relatively few people know Ruby, end even fewer people know Perl, especially among those younger than 50.

BTW Haskell is also syntactically compact and expressive; I once solved a toy interview problem using it. But even fewer people know it at a practical level, among interviewees and interviewers alike.

I’m proficient and confident in many languages because I like solving problems with computers. However in a high-stakes and low-tooling situation like a job interview I wouldn’t choose JavaScript any day of the week because of the uncertainty, inconsistency, and footguns it likes to sprinkle everywhere.

There is absolutely a role for JavaScript, it truly does run everywhere which is no small feat. But that’s about all it’s good at.

What I’m getting at is that when you give people a choice they are going to optimize for the scenario, so you have a heavy selection bias in your anecdote.

Is it a recruiting company, as it's main business is to recruit candidates ? Or was the core business something else and you were recruiting devs for it ?

I think it's always difficult to get the bigger picture of the dev market through candidates. Many (rightfully) handle their career moves by themselves and will directly apply to companies that fit them, heavily biasing the movement observed from the company or third party recruiters.

Main business is (was) recruiting people for roles all over the place.
Maybe a lot of things start in Python, because so many people know it, and no one wants to change languages after a project starts?

Python also has a very wide range of applications so while there are many languages more popular in a particular area, Python is extremely widely used in aggregate. By contrast it is unusual to write numerical code in PHP or web apps in C.

Python seems to be popular for solving leetcode style interview questions, which might be something worth of note.
I could probably say "you're moving the goalposts here, I'm taking all developers not all working developers" or something, but I think these days you're basically correct. Python was relatively more niche when I picked it up in 2009 or so, but it's really become the lingua franca of our industry.
My big use case for this is that it's quite good for glue scripting when you're already in the context of a JS shop. My common use cases are tooling setup, CI scripts, and API response sanity checking.
(comment deleted)
This feels like it could be learned in an hour and looks so nice. Wonder if it can be used with inline code to process line by line stdin.
Maybe the example doesn't do justice but it looks more complicated than simply using "&" and "wait".
Yup, it seems the perfect example to wonder if a language with async by default is appropriate for the use cases of bash scripts.

The example runs 7 commands and has to explicitly await for 4 of them. The other 3 can run in parallel and it awaits for the end of all of them, which is fair.

The typical bash script is sequential and I fear that its zx replacement will be an exercise of writing await, await, await.

I'd go with an await by default language and wrap parallel code in whatever that language offers to wait for the end of child processes.

Please don’t make me install Node.js to run your shell script.
Yeah that wouldn't be great.

But:

This feels to me like it might be a really useful tool to do systems-integration work around an app that is built in Node.js. For example, to integrate with incrond or cron, or get a supervisor task into JS. The smallest sensible "shell-ish" wrapper that gets you access to your codebase in JS and stops the coding effort being divided so much between technologies.

Sure. But I can easily see this becoming the default way to write shell scripts in the near future in the same way Electron is becoming the default way to write desktop applications.
I'll be honest, I did not pass the word Javascript. And I don't think I could be convinced to.

Didn't expect people to be so hurt by the truth. Sorry. I just don't trust JS running on my machines.

(comment deleted)
ZX?

Is there a rainbow bar over the bottom right corner?

Do words appear when you press each key?

"[For writing complex scripts] JavaScript is a perfect choice".

Not sold on that. Why should I choose it over Python, PHP or others?

It's asynchronous, which is pretty neat
A script, by its very nature, is synchronous. You run, you get a result. If you want a service, don't use a script.
This is pedantic to the point of being conversationally hostile.
That’s a completely arbitrary limitation which should not exist. When my scripts contain independent non-instant subprocesses, I run them in parallel. I’m also using js/ts to write all my scripts, because why wouldn’t I use a proper programming language instead of that “bash” thing that can’t even handle its single datatype well and resorts to all sorts of gibberish to perform trivial operations on it.
How? Pipes are not synchronous
yea same here. I wrote nodejs programs for years. the focus on the eventloop and everything being asynchronous is great for servers and network IO but does NOT make it the perfect choice for scripts. Its a different use case entirely.

scripts are supposed to be small simple programs that run in sequence and terminate. I shouldn't need to deal with concurrency primitives at all in that particular situation.

With modern JavaScript (as in last 5-7 years) it's pretty straightforward to write sequential scripts.
There's more to it than writing a simple sequential python, ruby or perl script. You have to write everything using async/await.
async/await are dead simple. And many Node libs have synchronous versions. It's pretty straightforward if you are familiar with the language. If you aren't, well that isn't really the fault of JS nor any different from bash or python, which can both be just as painful for those unfamiliar.
> scripts are supposed to be small simple programs that run in sequence and terminate. I shouldn't need to deal with concurrency primitives at all in that particular situation.

I suppose bash pipes are a concurrency primitive, but I don't object to them. I think it's more about appropriate primitives.

Especially because these languages are only one package/install away and not two. I don‘t really get for which audience is targeted here. Usage in JS projects maybe, but then why not write it as npm tasks. ..

I‘m playing around with dotnet-scripts [1] at the moment (C# shop mainly) and this has the same issue imho. The reason why I looked into it was because we have developers not accustomed to bash etc. I still find it silly and would rather use ruby so…

[1] https://github.com/dotnet-script/dotnet-script

> why not write it as npm tasks.

Do you mean writing the entire script in a single line in a string in a JSON file (package.json)? Of do you mean that string just calling out to a non-inline script? Because the use case for this is very much that latter script - it will often still be started via `npm run`.

Because you (i.e. I) already use it and have the full toolchain set up for your project?
100%

Use whatever you're proficient in. For A LOT of devs that means js.

Why spendtime pulling python into my js project?

(comment deleted)
Modern js is not what it was. Ergonomics of nodejs v20 are not in the same ballpark as node v6. A lot has happened since v12 even.

Js definitely has become a pleasant language to script in. And it's sooooo much faster that python and Ruby.

> And it's sooooo much faster that python and Ruby.

That advantage becomes vanishingly small when the language task is to glue together a bunch of spawn/fork/exec to external processes.

If the glue code somehow needs actual heavy processing, that processing can be factored out into another program dedicated to that, leaving only glue code. Side effect: the heavy processing code immediately becomes composable!

Still the same footguns for the sake of backwards compatibility. Things like:

  typeof '' == 'string'
  false
And many others that I'm are too depressing to mention. This is the latest one that I got surprised with.

Yes, it's fast but so is Lua, Dart or even Racket.

Can I qoute my comment verbatim back at you?

Seriously. Just don't use ==. Use ===. Et voila you're using a modern pleasant scripting language.

Modern guides on js won't ever mention ==. As a noob today you won't ever learn it. In a big project there will be a linter to stop you

wha?

    Welcome to Node.js v19.9.0.
    Type ".help" for more information.
    > typeof '' == 'string'
    true
???
> Why should I choose [JavaScript] over [...] PHP [...]?

LOL dude

made my day

PHP is a surprisingly capable scripting language. Some relatively recent version is installed by default on a lot of Linux distros and its standard library has tons of functions for dealing with strings, files, and the network.
Thanks, I hate it.

* I love the idea, and I'm interested to see more progress in this direction.

* I'm not a huge fan of the syntax - for me, the commands are the star of the show, while the async/await is a glue. This syntax emphasizes the async/await while the important stuff get shuffled into quotes.

* Don't make me install node - and especially not system/user-wide packages - to run a script.

Having said all that, this is a neat idea and a cool hack on top of javascript language features.

When my shell scripts get too large, I usually switch to Perl. Still best in class at handling text processing with a few shell commands here and there.
I'm doing that with Ruby, and tgrought the years, it became also true for scripts that are fetching data, iterating over JSON objects, etc.
What makes Ruby great for this is you can specify dependencies inline

  require 'bundler/inline'

  gemfile do
    source 'https://rubygems.org' 
    gem 'json', require: false
    gem 'nap', require: 'rest'
    gem 'cocoapods', '~> 0.34.1'
  end
I do the same, in Python. I do my best to keep it zero-dependency to maximise portability.
No way. If your script requires something more complex than what is difficult to implement or maintain in bash, then it is no longer just a script. Regardless of how small the resulting application may be, you should treat it as an application and not merely a script. Node.js is not typically used in the system layer where Bash scripts are commonly employed. No single administrator would replace Bash with Node.js. For parallel execution and asynchronous operations, tools like ansible exist, along with python, which administrators are already familiar with.
Bit verbose for me with all that promise/await stuff. Substitution rules probably simpler tho
How does this compare to the recently released Bun Shell?
One (admittedly obvious) difference is that it runs on Node/V8 instead of Bun/JSC.
can you elaborate the difference, besides the different names of the runtime?
I think you’ll find far better answers than I can provide with a web search.
I didn't which is why I asked
Bun Shell implements a cross-platform bash-like shell, using builtins for popular commands like `rm`, `cd`, `which`, `ls` etc which support the same flags on Linux, macOS, and Windows. A lot of why we made it was for `bun install` to work on Windows without needing polyfill packages like `rimraf` or `cross-env` in package.json scripts.

zx uses the system-provided shell, which means it isn't cross-platform

For the commands Bun Shell implements as builtins, they typically run something like 10x - 20x faster than in zx on mac/linux because spawning processes is expensive (even more expensive on Windows)

But...why?
WTH is this, some training program how to write await for no reason? :)
I just feel relieved that the majority here also seems to think this doesn’t make sense
It makes most sense if you're already using js and you need cross platform support in your scripts.
Useless use of cat in their example script.

This is not a serious piece of software.

Maybe the example is intended to be familiar to people who are not script experts?

(rather than an example of how to do things perfectly, according to rabid accolytes of the church of demogification?)

The first command of the first example is a cat|grep... How can I assume the author is an experienced script writer?

In any case, ZX replaces some of the shell's usual quirks with its own different quirks: you have to manually expand * with glob() and ~ with os.homedir(), you can't build commands as strings without repeated calls to push(), etc.

> example is a cat|grep... How can I assume the author is an experienced script writer?

Perhaps because they are writing documentation that can be understood be less experienced script writers as well as themselves, using a very common example seen everywhere?

I find "cat full | ..." keeps the left-to-right flow of a command nicely, and is better understood by those less familiar with shell work than prepending "< file " which I've seen cause confusion.⁰

Yes, it is an extra process. Yes this is less efficient, both due to the fork and the extra IPC along the pipe. But even under Windows where spawning a process is massively more costly than other OSs¹, if you are that worried about this bit of inefficiency² then perhaps you have picked the wrong tool for the job in using a shell/script in the first place?³

> with its own different quirks

I agree there. Though I can see that for some, starting from already using JS via node, this might be an acceptable trade-off.

--

[0] I sometimes also use it in part to wind up the cult of demogification, because they seem to both want to be wound up and deserve to be wound up :-)

[1] Complain to MS about that, not me and my script/one-liner.

[2] Are you using it in a tight loop? Maybe if you are doing something over thousands of files, but then the resources used by the something likely dwarf the cost of cat into irrelevance.

[3] A question that suggests the supplementary: are you an experienced <whatever>?

> I find "cat full | ..." keeps the left-to-right flow of a command nicely

Sorry, I don't buy it.

You don't read files like this:

  cat file | less
And you don't compile C programs like this:

  cat hello.c | gcc -xc -
Actually using less, grep (and even gcc) as filters is more confusing to "those less familiar with shell work".
For a single command acting on a file, especially with no other parameters, like your less example, you are right and I wouldn't, though I have seen it often. There is practically no "left-to-right" flow to break. Longer examples is where I would use it.

Also not in circumstances where explicitly acting on a file like your GCC example. It is unlikely that GCC will commonly be used as part of a more collect pipeline of commands.

Note that I said "I use it", not "I always use it, without fail, no other option makes sense ever". Different tools, different jobs (this is what the Church of Latter Day Demogifiers usual don't accept in this context).

Neither is bash great, nor is JavaScript a prefect choice, and the examples are pretty underwhelming with all the inconvenient escapes and keywords
Well this doesn't make much sense.

If you rightfully find bash inadequate at some point, just switch to the next most natural tool - Python. Nobody's going to be surprised when they see a Python script.

If you start feeling Python is inadequate too, then:

1) You're wrong, just stick to Python

2) If you're so bent on doing away with familiarity, at least get as much leverage as you can in return and use a "proper" language, like Go / Rust. You get static typing and easy to deploy binaries, which are solid benefits.

Trying to use JS here is the worst of both worlds. Nobody expects to see JS in this context and it doesn't bring much to the table.

Python has been banned in many places for this sort of thing in my experience. There is just too much shite and baggage associated with python environments for it to be reliably portable between systems. Anaconda that, pip this, 2.x, 3.x, string support. It's a dumpster fire.

I am not saying JavaScript is any better, but python is actively considered harmful for this use case in many places I have been.

in my case im usign groovy scripts with grape dependencies. script is always selfcontained single file. max portability in JVM world. no need to fiddle with dependencies.
Honestly this looks better than Python. I hate all the subprocess.run([...], check=True, universal_newlines=True) and then you have the manage the dependency hell of virtualenvs. At least with node the node_modules is right there.
Yes, but this isn't made for general scripting. This project seems to be aimed at JavaScript projects, for example calling zx from package.json/scripts/start

If I had a JavaScript project, and I wanted to migrate my scripts away from Bash, I wouldn't pick Python. Not only would it be another language, I would also have to configure Python to run correctly, ie add it to my Docker image.

Simply installing another NPM package, rather than include an entire other language is a much better and simpler solution.

Why should a nodejs dev have to pull in python? That is a heavy solution when you can script well in js already.
Python's ok for this as long as you need 0 third party dependencies and it's a single file. As soon as you want to split into multiple files or import something that isn't part of the standard library then it becomes a total disaster.

I think Deno is a really good option here - better than ZX. You can use third party libraries easily, it works with IDEs, and it's trivial to install.