Live data from Hacker News

Hush, a modern shell scripting language

hush-shell.github.io

131–140 of 192 posts

Re: Hush, a modern shell scripting language

#131

Earlier quoted context omitted.

I love Python, but if all I need to do is grab one part of one line of something, and put it into some other command, then I'm just going to use some unholy mixture of sed, awk, or whatever. If I did the same thing in Python, I'd end up with thirty lines or more.

Thirty lines? Unholy one liners are not limited to shell scripting: from subprocess import check_output sh = lambda script: check_output(script, shell=True, text=True) ips = set(line.split()[-1] for line in sh('last -a').splitlines() if line and 'tmux' not in line) The first two lines are pure overhead. The last line is the equivalent of a shell script one liner but now has all the advantages of a language that suppo…

> Unholy one liners are not limited to shell scripting

You’re mostly proving the parent’s point here. Yours isn’t a one liner, and even in Bash people don’t write multi-line for loops in one line. I don’t know what you mean about the first two lines being overhead; this doesn’t work without them, and you have magical options “shell” and “text” in there that matter. Your script doesn’t pass the result to another process, so you need another line. This also splits output on spaces, it should be more generic. Doing that involves another module (regex), another function call (or more likely several), and several more lines.

Compared to “last -a | grep tmux | process”, your script is much closer on a log scale to 30 lines than 1, even if the parent’s 30 is a little exaggerated, and even if we use the 5 lines you showed and not the ~10-12 lines it really would be.

Re: Hush, a modern shell scripting language

#132

> Traditional shell scripting languages are notoriously limited I feel like people looking to replace shells and shell languages need to really think deep and hard about this if it's something they believe. Shell scripts are really anything but limited, and in fact most replacements are more limited (either by design or by accident), often imposing awkward control flow on you or making things that should be simple mu…

Yes, Python comes with 100% overhead from Shell. I have packages inside either the system or virtual environment tools. Honestly I err toward sh/bash because I write it, ship it, and forget it!

Re: Hush, a modern shell scripting language

#133

If this wants to sell itself as a shell scripting language, it should very quickly advertise what is it that makes it superior to say, bash for typical shell scripting tasks. Shell scripts with bash are painful to the point that if I find myself writing more than around 10 lines of shell, I tend to stop and switch to Perl instead. But Perl hasn't been too popular lately and isn't ideal either, so I'm very much up for…

> 100% reliable file iteration. I want to do a "for each file in this directory" in a manner that doesn't ever run into trouble with spaces, newlines or unusual characters.

In UNIX file names, there is exactly one unusual character: ASCII NULL (or '\0'). Every other character is usual, including spaces, newlines, tabs and other historical ASCII control characters.

------

> No length limits. If I'm processing 10K files, I don't want to run into the problem that the command line is too long.

Shell does not impose length limits, the UNIX kernel does by defining the limit of how much can be passed into the «execve» syscall which UNIX shells use to create new processes. You can find the length limit on your system by running «getconf ARG_MAX» in the shell as it varies across different systems (it varies even across different versions of the same system; in Linux, it is now reportedly 1/4th of ulimit -s). The command line limit is calculated using following constituents:

  command line limit = «environment size (env | wc -c)» *plus* «(ARG_MAX *minus* environment size» *minus* «POSIX recommended 2048»).
How the length limit works is explained at length (please pardon the pun) here: https://www.in-ulm.de/~mascheck/various/argmax/

------

> Excellent path parsing. […] finding the file extension […]

File names in UNIX do not have extensions, they simply have names. Is «.bashrc»: 1) a full and complete file name or 2) an empty file name with the «bashrc» extension? It is (1). Moreover, any valid character can be used as a separator and its interpretation is either left out (almost always), or the interpretation is left up to the semantically aware app. One is free to use a comma or the Javanese wasana pada as part of the extension; that is, «file,exec» and «file꧅ ꦆ ꧅exec» are both valid and both have the extension of «exec» – as long as the file system supports the appropriate character set. This is also why «basename», to work correctly, requires the separator as part of the imaginary «extension», i.e. «basename myfile,exec ,exec» will always give «myfile» as the result.

DOS style extensions are a made up convention that neither the kernel, nor the shell, nor file processing utilities enforce as they are file extension unaware. It is better to think of UNIX file names as being made up a prefix and an optional, arbitrary length suffix (but not an extension).

------

> Excellent process management. We're in 2022, FFS. We have 128 core CPUs. A modern shell scripting language should make it trivial to do something like: take these 50000 files, and feed them all through imagemagick, using every core available

With respect to «every core available». You almost certainly don't want that and should overprovision the number of running processes compared to the number of cores on your system.

UNIX has supported multiprocessor systems for a very long time. Despite massive improvements in the hardware performance, disks and networks are still the slowest moving (or still) parts. They were even slower when UNIX was in its relative infancy when the CPU time was also very expensive. Therefore, the CPU time had to utilised efficiently whilst waiting for a disk to return a string of bytes.

File processing tasks spend their time between: 1) waiting for I/O (in the blocked state) and 2) actually processing (the running state). Since the disk is still very slow compared to the speed of a modern CPU, the UNIX process scheduler blocks the process until I/O completes and checks the process run queue in the kernel to see if there is another process ready to compute something (i.e. in the running state). This inherent interleaving of «blocked for I/O» and «running» process states can be used to an advantage depending on a few factors.

The less is size of the unit of data an app processes and the larger is the total size of the input (i.e. the input and output files), the more time the app spends in the «blocked for I/O» state, and most of the CPU time is simply wasted unless there is something else to do. But if we know such specifics (the size of the unit of work and the size of the input), we can overprovision the number of processes thereby utilising the CPU compute time more efficiently whilst the disk controller is transferring bytes into the memory via the direct memory access (I am oversimplifying a few bits here). This is the reason why «make -j12» will compile almost always faster than «make -j8» on a 8 CPU core system on projects with a large number of small(-er) files – because of the I/O overhead. Whilst there is no universal formula, 1.5x process overprovisioning factor is a decent starting point. For simpler daily file processing tasks GNU parallel is good enough to spare oneself of headaches of such computations, though.

Therefore, the «We're in 2022, FFS. We have 128 core CPUs. A modern shell scripting language should make it trivial to do something like: take these 50000 files, and feed them all through imagemagick, using every core available […]» does not make sense in the context of UNIX shell languages and the process scheduling in UNIX. In fact, you will underutilise your 128 CPU cores, sometimes pretty heavily, unless you correctly account for the I/O factor.

Only if the app/process is aware of how to efficiently parallelise its own workload (because it knows its workload better than anyone else), then and only then it does need to know how many cores there are available at its disposal. No scripting language / shell can solve this problem.

------

> Excellent error reporting. I don't want things failing with "Command failed, aborted". I want things to fail with "Command 'git checkout https://....' exited with return code 3, and here's for good measure the stdout and stderr even if I redirected them somewhere".

The excellent error reporting has existed in UNIX since day 1. It is called the process status code; anything that is not a zero status code indicates an error. The semantic interpretation of each speific numeric status code, however, is entirely decoupled from the thing that might fail and is documented in the man page. The status code of 3 in «mv» and in «git» will mean two completely different things, therefore the specific status code processing is localised to the process invocation point in shell scripts. Most of the time, though, I personally don't want my shell script to explode with an error message from a random failed command unless it is something of extreme importance to me; checking for the process exit code and acting upon it accordingly is sufficient and is good enough.

Whether such an approach is a good thing or not is a matter of a debate. Global lists of errors and/or error messages require an official register of both diligently maintained and updates of which to be centrally coordinated, which I don't think could work with the open source.

Other operating systems have attempted to mandate error codes with complex structures and a well defined (and sometimes written by a professional technical writer!) error message. Yet, they have had limited success. Yes, OS/400 running on an AS/400 could inspect a failed process' error code and automatically dispatch a message to an IBM service centre to order a spare part for a specific failing piece of hardware whose SKU would have been deduced from a specic part of the error code without requiring the human intervention, but that is somewhat of an extreme and extravagant example and is certainly not mainstream.*

EDIT: «make -j12» vs «make -j8» explanation.

Re: Hush, a modern shell scripting language

#134

> Traditional shell scripting languages are notoriously limited I feel like people looking to replace shells and shell languages need to really think deep and hard about this if it's something they believe. Shell scripts are really anything but limited, and in fact most replacements are more limited (either by design or by accident), often imposing awkward control flow on you or making things that should be simple mu…

>the reason shell scripts have endured is precisely because they are extremely good at that

It's very easy to make something extremely good at this and much less error prone. That's the main problem.

>often imposing awkward control flow on you or making things that should be simple much much more complicated.

There's literally one primitive paradigm for control flow, both bash and other languages incorporate it. if... else. <----That''s it.

Re: Hush, a modern shell scripting language

#135
post #98

Earlier quoted context omitted.

I couldn't agree with this statement more. As someone who has developed their own shell over the last decade I've been keeping a close eye on what other people have been building too. So often I see people writing shells that are inspired by programming languages so their syntax looks amazing in documents. But they always strike me as being hugely tedious for repetitive and often quite dull tasks. The kind of 5 minut…

> We need better shells. Obviously. I don't think this is the most important missing part, though. I would say it differently: we need way, way better REPLs. IPython is an example of a REPL that's passable as a shell. It can run in a terminal and has a GUI version based on Qt, which allows displaying images inline. You can drop into a "real" shell with a single `!` character (you get pipes, output capture, and (Pytho…

The REPL is the shell. I even used the term "REPL" (and a couple of synonyms too) in my comment. So I do agree it's critical but that doesn't make the language irrelevant. Your point about how Python shells have had to create syntactic sugar for REPl usage is a good illustration of my point about how it matters a lot.

Also you can render images in quite a few terminal emulators already. Some shells (mine included) ship with hooks to autodetect which terminal emulator you're using and find the best method for rendering those images. eg https://github.com/lmorg/murex/blob/master/config/defaults/p...

There's definitely room for improvement in the whole TTY / shell / terminal emulator integration space though. But that's not going to happen without breaking support for existing CLI tools. Which basically means it's never going to happen given it wouldn't ever gain traction.

Re: Hush, a modern shell scripting language

#136
post #4

Is this really a shell scriping language? Hush isn't an interactive shell, nor does it compile to a common shell script. The only way to run these scripts is to install the hush interpreter and run the script through it. Isn't that just a normal scripting language? What's the real benefit of using this over Node or Python? I suppose the syntax is more aesthetically similar to shell scripts... but I don't exactly see…

I love Python, but if all I need to do is grab one part of one line of something, and put it into some other command, then I'm just going to use some unholy mixture of sed, awk, or whatever. If I did the same thing in Python, I'd end up with thirty lines or more.

Check out marcel (https://marceltheshell.org). Marcel is a shell that allows the use of Python functions, and pipes Python values between commands. E.g. remove the files/directories listed in victims.txt (one per line):

     read victims.txt | args [f: rm -rf (f)]
Parens delimit Python expressions, so (f) just returns the value of f, one of the values read from victims.txt.

Marcel also provides a Python module so that Python can be used for scripting much more conveniently than in straight Python (i.e. without the marcel module). E.g., print the names of recently changed files in the current directory:

    import os
    from marcel.api import *

    for file in (ls(os.getcwd(), file=True, recursive=True) |
                 select(lambda f: now() - f.mtime 

Re: Hush, a modern shell scripting language

#137
post #54
post #8

Earlier quoted context omitted.

The point is that you can do things like create external processes, pipe them together, redirect output to files, with the same ultra-lightweight syntax of bash etc. Compare that to all the nonsense you have to do in Node or Python to pipe two processes together!

This is a thing: https://pypi.org/project/plumbum/

This too: https://marceltheshell.org.

Re: Hush, a modern shell scripting language

#138

Earlier quoted context omitted.

> I don't like .NET very much, so that's a downside for me. LOL. So arbitrary... do you like java vm or python? > Can PowerShell use the same CLI utilities as Bash, and do the plain-old-text-stream processing pipelines like Bash, or is it .NET objects only? Sure. Even better, it can convert wall of text into array of lines on the fly :) You should forget about parsing text in shell though. Because it sux and we know…

Yeah? Well, you know, that's just, like, uh, your opinion, man.

Yours is not even opinion. You need to put some logic behind it man :)

As for me, I will keep my Decade-And-More-Old-And-Still-Shiny-Shell-With-Objects-And-Stuff to myself :)

Re: Hush, a modern shell scripting language

#139
There's no interactive shell from what I can see. How does this qualify as shell scripting?

The point of a shell is that it's how I interact with the system. The point of scripting that is to automate my interactions by storing the exact same commands I type and sometimes adding a bit of logic around them. Hush is not that.

Re: Hush, a modern shell scripting language

#140

Earlier quoted context omitted.

I love Python, but if all I need to do is grab one part of one line of something, and put it into some other command, then I'm just going to use some unholy mixture of sed, awk, or whatever. If I did the same thing in Python, I'd end up with thirty lines or more.

Thirty lines? Unholy one liners are not limited to shell scripting: from subprocess import check_output sh = lambda script: check_output(script, shell=True, text=True) ips = set(line.split()[-1] for line in sh('last -a').splitlines() if line and 'tmux' not in line) The first two lines are pure overhead. The last line is the equivalent of a shell script one liner but now has all the advantages of a language that suppo…

I don't have exact rules, but if it's (1) quick, (2) simple to read/write in shell, (3) separate from my app's business logic, then I'll use a shell script.

For example, to take a line off of the end of a file, sed is pretty easy:

    sed -i '' -e '$ d' file.txt
After a few pipes, I will reach for Python. Regarding line count, after a `if __name__ == '__main__':` block and some helpful docstrings, it ends up being about thirty lines or so. "\N{man shrugging}"
Post reply on HN