Live data from Hacker News

How to write idempotent Bash scripts

arslan.io

111–120 of 195 posts

Re: How to write idempotent Bash scripts

#111

I got hung up on the claim "Touch is by default idempotent. ... A second call won’t have any effects". The whole point of touch is to have an effect every single time you run it. It updates the file atime and mtime. That may seem harmless in your application, but it's definitely not no effect. Also promiscuous touching is the source of a bunch of bogus last modified dates in source code bundles.

I've heard Idempotency confused with Consistency. Idempotency is where a function f can be applied (as a composition) multiple times and it gives the same result, so Idempotency: f(f(x)) = f(x) . Whereas Consistency: f(x) = f(x) = f(x) . An example of an idempotent function f is RaiseToThePowerZero(x) on x > 0. We usually take consistency for granted, so it's perhaps better to think of an example of a non-consistent…

> as a convolution

Sorry, what does idempotency have to do with convolution?

Re: How to write idempotent Bash scripts

#112
post #53

Earlier quoted context omitted.

I feel like my comment went in one eyeball and out the other. Chef and friends are blocked on disk I/O. How does a typesystem and/or thinner abstraction layer to disk I/O speed up the underlying expensive operation: blocking disk I/O?

I - on the other hand, feel like your comment somehow forgot that the op said “safety, speed and convenience”, so you attacked the least important point made in the first place. It’s much easier to see the failure conditions in a Rust program rather than in bash. Also rust seems like easier to maintain, too.

Rust would be a step up from writing bash scripts of yore—imagine, being able to tell the difference between a variable being empty vs unset—but really any non-stringly-typed language will get you that, and the GC-based ones are unlikely to have memory errors and are generally fast enough for this purpose.

I don’t think there would be anything wrong with a rust implementation of infrastructure management, but I also don’t think it’s the silver bullet to solve what plagues the space.

Re: How to write idempotent Bash scripts

#113

Earlier quoted context omitted.

I've heard Idempotency confused with Consistency. Idempotency is where a function f can be applied (as a composition) multiple times and it gives the same result, so Idempotency: f(f(x)) = f(x) . Whereas Consistency: f(x) = f(x) = f(x) . An example of an idempotent function f is RaiseToThePowerZero(x) on x > 0. We usually take consistency for granted, so it's perhaps better to think of an example of a non-consistent…

> as a convolution Sorry, what does idempotency have to do with convolution?

Should be composition, not convolution, my brain fart.

Re: How to write idempotent Bash scripts

#114
post #91

Earlier quoted context omitted.

I - on the other hand, feel like your comment somehow forgot that the op said “safety, speed and convenience”, so you attacked the least important point made in the first place. It’s much easier to see the failure conditions in a Rust program rather than in bash. Also rust seems like easier to maintain, too.

How does having a borrow checker and snazzy memory safety benefit you when nearly all the operations you're performing are disk I/O? How is a 100 lines of rust easier to maintain than 10 lines of shell script? It's exactly what the other commnet said: "Writing a shell script in rust would be obnoxiously difficult for no real benefit."

Well, for once Rust's type system can enforce the usage of objects (e.g. temporary file must be used or deleted).

>> How is a 100 lines of rust easier to maintain than 10 lines of shell script?

In 10 lines of _proper_ bash you won't be able to even check if the arguments supplied to the script even exist, let alone parse something more than simply subscripting argv[].

Re: How to write idempotent Bash scripts

#115
post #4

Many people who write scripts do not factor the environment they are run in and miss including traps to handle events like being terminated mid-way thru and cleaning up to a sanitized state. After all, creating a file, you can't presume their is enough free space, but people do in scripts all the time. Then you have scripts that you want run once and if run again not have any more effect. FOr example a script to be r…

> An easy way to cover that is to have the script create a file, which you check at the start of the script and if present you exit.

This does sound a good solution on paper, but "checking at the start" and "creating a file" are two different steps (i.e. not atomic) and will cause trouble eventually if your system has the tendency to run the script twice. A better solution is to use the `flock` command before creating the flag file. For example,

  exec 999>/var/lock/my.lock
  flock -n 999 || exit 1
  if [ ! -f flag_file ]; then
    echo "script not ran before, running"
    touch flag_file
  else
    echo " already ran, exiting"
    exit 1
  fi
  # do stuff here

Re: How to write idempotent Bash scripts

#116

I got hung up on the claim "Touch is by default idempotent. ... A second call won’t have any effects". The whole point of touch is to have an effect every single time you run it. It updates the file atime and mtime. That may seem harmless in your application, but it's definitely not no effect. Also promiscuous touching is the source of a bunch of bogus last modified dates in source code bundles.

I've heard Idempotency confused with Consistency. Idempotency is where a function f can be applied (as a composition) multiple times and it gives the same result, so Idempotency: f(f(x)) = f(x) . Whereas Consistency: f(x) = f(x) = f(x) . An example of an idempotent function f is RaiseToThePowerZero(x) on x > 0. We usually take consistency for granted, so it's perhaps better to think of an example of a non-consistent…

If you count the filesystem stats on a file as part of the system state, it is certainly not idempotent. You need to be aware of that effect on system state before you can decide whether to ignore it.

Idempotency requires that the function map from a space back onto itself. Touch is only roughly idempotent in that you can define a statistic S on the overall system state discarding filesystem stats such that S(f(f(x))) = S(f(x)).

Re: How to write idempotent Bash scripts

#117
post #42

Earlier quoted context omitted.

How is that related to the article aside from "both involve bash"?

It's because rm -rf "$STEAMROOT/"* will evaluate as rm -rf "/"* if $STEAMROOT for some reason is blank or unset, which is what happened here. (As someone in that discussion mentioned, a little more Bash knowledge might have suggested using rm -rf "${STEAMROOT:?}/"* instead, to force it to (at least) error if it is empty or unset.)

So literally the only thing in common with the article is that they involve bash

Re: How to write idempotent Bash scripts

#119
post #22

Earlier quoted context omitted.

Chef isn't magic, it just uses a lot of cookbooks which are just running things to check what the state is. Usually those cookbooks will want to have things in a particular way, which might not be the way you want. Chef/Ansible/Puppet all have the problem of having many layers of overhead for the same thing. This makes them slow, hard to debug, hard to read and even write (in my opinion). Sure, bash is hell, but the…

Rust doesn't get you anything resembling what you want here. Chef/Ansible/Salt could be described as rudimentary type systems for operating system states, that enable you to convert between different types/states. It's conceivable you could build such a system in Rust, but Rust itself has no primitives that would make it easy. It certainly isn't a good starting point when you want to set up a database server.

All Config Management systems execute other programs, parse their output, check/parse files underneath their fancy skin.

I've used both Chef and Ansible. Their maintenance cost is pretty high unfortunately, and they are not that flexible, nor resilient to worth it.

No wonder the container/k8s boom is so big. Immutable infrastructure (docker images) with really powerful operational features is what can justify high maintenance efforts. Whereas fancy but brittle (due to the inherent problem of state discovery) CM systems are at best only useful for the initial platform setup.

Rust is just useful because you can quickly and relatively safely produce single binary programs. (Many people use go for this, but it's easier to skip error handling in go which result in runtime problems, which is very inconvenient in a provisioning/setup system.)

Re: How to write idempotent Bash scripts

#120

I got hung up on the claim "Touch is by default idempotent. ... A second call won’t have any effects". The whole point of touch is to have an effect every single time you run it. It updates the file atime and mtime. That may seem harmless in your application, but it's definitely not no effect. Also promiscuous touching is the source of a bunch of bogus last modified dates in source code bundles.

In other words: When promiscuous touching, always make sure you’re not making things unsanitary.

Please don't do this here.
Post reply on HN