6

Am new to shell scripting, and it makes me wonder why the following snippet does not work in zsh but does in bash? Is there a zsh equivalent of the same snippet? I get error parse error near `200'

( 
  flock -e 200
  echo "In critical section"
  sleep 5 
) 200>/tmp/blah.lockfile 
echo "After critical section"

The above snippet of code is from src: https://stackoverflow.com/a/13551882/15087532

codepoet
  • 586
  • 1
    Please see this https://unix.stackexchange.com/a/475427/487543 – Alex Sep 09 '21 at 19:54
  • @Alex Thanks, but that still doesn't give a clear and complete snippet of code. – codepoet Sep 09 '21 at 19:58
  • Use a fd in the range 1..9. – schily Sep 09 '21 at 20:47
  • 1
    @schily I would prefer not to hardcode a number, online forums seem to suggest that the number 200 was chosen in example as its unlikely a script would have opened as many files. Is there a automatic way of getting a file descriptor? – codepoet Sep 09 '21 at 21:53
  • 2
  • 3
    You can only rely on file descriptor numbers below 10 to be available in a portable script. Also zsh has a built-in flock in its zsystem module that would be better to use in zsh scripts. – Kusalananda Sep 10 '21 at 05:40
  • 1
    Even in bash or yash (the two Bourne-like shells I know that allow using fds above 9 explicitly), doing it is dangerous as if you happen to use a fd that the shell uses internally that will fail (with an error in yash, silently in bash). yash does extent the range of fds you can safely you to 0-99 though. – Stéphane Chazelas Sep 10 '21 at 05:57
  • @reportaman sorry, but I know of no such method. – schily Sep 10 '21 at 11:16
  • @schily Read my answer. Now you know. ;) – Marlon Richert Sep 10 '21 at 11:17
  • @StéphaneChazelas correct, you need to know that in 1976 when the Bourne Shell was written, no more than 20 fds have been possible in total. permitting 10 of them thus seems to be OK, The Bourne Shell dup(2)s fds to fd #19 and below for internal parking purposes in order to be able to restore a previous state. I don't know the numbers bash uses for that purpose, but if you choose one of these fds, this would destroy the usability of that shell process. – schily Sep 10 '21 at 11:21
  • @MarlonRichert I of course know that method, but it is non-portable and thus not really useful. – schily Sep 10 '21 at 11:26
  • @reportaman Do you think you could Accept my answer? – Marlon Richert Dec 14 '22 at 18:00
  • @MarlonRichert There are 2 parts to my question. Your answer is not descriptive enough for the first part: "it makes me wonder why the following snippet does not work in zsh but does in bash?" Am not looking for just a copy-paste-run snippet, but explanation. Other readers who have same questions would appreciate an explanation instead of just a snippet. – codepoet Dec 16 '22 at 21:56

1 Answers1

5

Don’t hardcode the fd. Let the shell acquire it for you:

(
  typeset fd=
  exec {fd}> /tmp/blah.lockfile
  flock -e $fd
  echo "In critical section"
  sleep 5 
)
echo "After critical section"

This works in Bash, too.

  • FYI, that feature was implemented in zsh, ksh93 and bash at the same time following discussion between maintainers of all three shells. IIRC, proposal was by Oliver Kiddle, one of zsh maintainers. – Stéphane Chazelas Sep 10 '21 at 11:21
  • In other words, this is a method that you definitely should not use if you like to create portable scripts that work with POSIX shells. – schily Sep 10 '21 at 11:25
  • 1
    @schily, given how the Q asked for a solution that works in zsh, while presenting one that works in Bash (only), that's hardly a fault in this answer. Also, I don't see your answer with a POSIX-compatible solution either. – ilkkachu Sep 10 '21 at 12:35
  • 1
    In one of the linked questions, there's was a version of this with ( flock -e $fd; ... ) {fd}>/some/lockfile, but for some reason zsh doesn't seem to parse that, even though it supports {var}>whatever on a simple command (or with exec, as here) – ilkkachu Sep 10 '21 at 13:26