Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

In those times I wish I could use the emacs lisp way:

    (let (dir "/foo")
      (create-directory dir)
      (with-current-directory dir
        (delete-all-files-recursively)))
Factor recognized the value of dynamically scoped variables: http://concatenative.org/wiki/view/Factor/FAQ/What's%20Facto...

A lot of code became much simpler because of that decision.



  $ mkdir /tmp/foo && cd /tmp/foo && touch bar.txt


Yes. Always chain sequential commands with &&. Always.

This style is pretty prevalent when writing test cases in shell (or just shell scripts in general), e.g. when using something like sharness[1].

[1] https://github.com/mlafeldt/sharness


"bash -e" has much the same effect: it basically exits on the first failed command, with considerations for pipelines, conditionals and the like.

http://www.gnu.org/software/bash/manual/bashref.html#index-s...


Note this recent discussion about bash -e, including my post that "the disappointment with set -e is that it does not work everywhere".

https://news.ycombinator.com/item?id=8054440


It's nice to have, but I wouldn't rely on it to save me - it's no replacement for checking return codes properly.


Certainly, but it's a good habit to get into, like ``use strict`` in Perl.


"Perl and line noise are distinguishable. Properly written perl starts with 'use strict'; line noise rarely does."


When writing test cases in shell, you generally just implement a die() function and execute all commands `foo || die "bar"`


If you don't need to specify a custom message, you could also use "set -e" to die automatically on command failure and use "trap ... EXIT" to display some kind of failure message.


    $ mkdir -m 0700


This isn't really fork() failing per se -- but rather a failed program/script that did not understand the well defined and clearly documented behavior of fork().


The man page says, "On failure, -1 is returned in the parent, no process is created, and errno is set appropriately."

So if fork is behaving as documented, returning -1 is because of "fork failing".


True -- I guess fork() has failed at that point. I was more getting at the article's authors scenarios of careless scripts treating -1 as a valid pid (which should always be > 0), which would be a failure of the script instead.


Well, it's a failure of the script and the POSIX API - it is unfortunate that a pid_t inhabited by -1 means "failure" in one case and "everything" in another. It is certainly not fork, narrowly, to blame.


I think the point that the author was making, outside of any criticism to the API itself, is that the hapless programmer may not know that fork() could even return -1. The article is pointing out that possibility as documented by the API, not criticizing a failure of the API. Whether or not the API is faulty in this--and I tend to agree with you--is outside of the scope of the article, though only just.


I wasn't making any particular comment on the content of the article. It was certainly relevant to the discussion tangent the thread had wandered down.


Taking the square root of a negative number removing all the files in your home directory could be "well defined and clearly documented behavior". Would you blame the API author at that point or would it still be strictly your fault?

At what point do API authors share the blame for a needlessly harsh punishment delivered upon a predictably common error?

I certainly prefer to work with systems produced by people tending to think it'd be their fault more often than not.


I could argue, however, that in this particular case, it's the user's fault for failing to understand the full and defined behavior of fork() in addition to failing to understand the full and defined behavior of other functions, ...say, kill().

It's just as wrong to feed kill() -1 as it would be to feed it -48585 or "babdkd" (unless that is explicitly your intention). A simple sanity check of if [ "${pid} > "0" ]; is all that's necessary to protect against this behavior.

So, I would argue, the fault lays on the users, not the creator, for not understanding the API when all materials necessary to understand said API are freely available.

(with that said, I think it's safe to say, we've all been bitten by not fully understanding some function before)


There'd be far fewer bugs if everyone knew exactly how everything else works.

This kind of mistake is godawful and should not be defended. (but it's correctly fixed through stronger typing, not through choosing -48585 as the code for killing everything).


> not through choosing -48585 as the code for killing everything)

Actually, only -1 is the code that "kills everything"

> There'd be far fewer bugs if everyone knew exactly how everything else works.

Perhaps I misinterpreted your meaning, because you seem to be advocating using programming and scripting languages without actually bothering to learn them. Of course this can, will and does lead to very bad effects.

The bottom line is, if you are going to use a function in your program/script -- please, read the docs and understand what is will return at the very least.


> Perhaps I misinterpreted your meaning, because you seem to be advocating using programming and scripting languages without actually bothering to learn them. Of course this can, will and does lead to very bad effects.

You're arguing that people should read the API before doing anything with it. Parent's point is that this class of error can be avoided by strong typing (eg, via algebraic datatypes), negating the chance that it would happen in the first place. Which, I think, is the right way to look at the problem. But certainly, if you do have to use a weakly-typed, unsafe language which does not provide this kind of guarantee, be sure to read the documentation twice.

Which doesn't mean you won't get bitten when it turns out that the person writing a library you rely didn't RTFM.


Fault is not a rivalrous good. It's the user's fault, and it's the API creator's fault.

Is there a reason fork can't be changed to just crash the program on failure? Are situations where a program usefully does something other than crash on fork failure, more or less common than situations where a program fails in the way described in the article?


I can't think of any good cases for crashing a program when fork fails. If you're doing work in a pool of processes and the parent process tries to fork another process, but fails, there are many ways to handle this: wait a few seconds before trying again, wait for another process to finish, kill a few processes, etc.

If you were forking in a high-level language such as Python, failing to fork would raise an exception which would possibly crash the program if left unhandled.

C does not have exceptions so return codes are used to indicate success or failure. This is true for nearly every function, not just fork. If you're not checking for errors in a C program, it's going to break in unexpected ways, and will possibly be vulnerable to exploitation.

fork has 3 possible return values: - 0 for the child process - a positive number for the parent process - a negative number if it failed.

If you look at the man page's "Return Value" section, it is extremely clear, see: http://linux.die.net/man/2/fork

"On success, the PID of the child process is returned in the parent, and 0 is returned in the child. On failure, -1 is returned in the parent, no child process is created, and errno is set appropriately."


"Is there a reason fork can't be changed to just crash the program on failure?"

YES. Situation: fork bomb, can't create new processes. How do you notice? Typically because you can't spawn new processes from your shell because fork is failing. With bash, there is a kill builtin - so you have a chance of cleaning things up (depending) if you have a shell open. If the failed fork kills the shell, then oops, you don't have a shell open.


That should be up to the programmer to decide. If fork() fails, it's almost always a transient situation that can be recovered from by spinning until fork() succeeds. Or one could have jobs doing useful things that can finish up, state saved, etc, for when the process is re-run. Just dropping everything onto the floor is usually the worst option.


Psst - it's "per se"...


¿Por qué?


I originally had "per-say" instead of the correct "per se"


;-P


I fail to see how dynamic scope pertains to the code you wrote or helps avoid the bug described in grandparent.

Specifically, the code you wrote would behave the same if `dir` had lexical scope.


The with-current-directory part is the dynamic part - i.e. within its dynamic scope, the current directory is now dir.

The scoping of the dir variable itself is irrelevant.


`with-current-directory` has to be a macro because if it is not, then the evaluation of (delete-all-files-recursively) happens before the definition of with-current-directory can change the current directory.

What the person meant when he wrote, "In those times I wish I could use the emacs lisp way," is, "In those times I wish I could use a lisp _macro_" -- particularly, one of those macros that makes a change, runs some code ("the body") then undoes the change.

Since all lisps have macros, the code above would work in any lisp -- not just Emacs Lisp. Among lisps, Emacs Lisp is famous for its dynamically scoped variables. Consequently, the specific reference to Emacs Lisp perpetuates the confusion that how variables are scoped has anything to do with what we have been talking about.


Yes, but emacs lisp is the only lisp I used, and lives in an environment where those macros are really useful.

    with-auto-compression-mode  with-case-table
    with-category-table         with-coding-priority
    with-current-buffer         with-decoded-time-value
    with-demoted-errors         with-electric-help
    with-help-window            with-local-quit
    with-no-warnings            with-output-to-string
    with-output-to-temp-buffer  with-selected-frame
    with-selected-window        with-silent-modifications
    with-syntax-table           with-temp-buffer
    with-temp-buffer-window     with-temp-file
    with-temp-message           with-timeout
    with-timeout-suspend        with-timeout-unsuspend
    with-wrapper-hook


What's wrong with the C version?

    char *dir = "/foo";
    mkdir(dir, 0700);
    if (chdir(dir) == 0)
        delete_all_files();


Nothing, of course. I just find the macros that give you a "modified environment" to run some code short and sweet.

with-temp-buffer is another example: a macro that bridges the functions for "string manipulation" and the ones for "buffer manipulation", since you start writing stuff this way:

  (defun replace-in-string (str from to) 
    (with-temp-buffer
      (insert str)
      (beginning-of-file)
      ;;; Here you can use all your normal text editing commands
      (replace-regexp from to nil t)         
      (buffer-string)))
Lots of dirty manipulation, but from outside its a pure function, and doesn't change the editor state in any way after it runs.


That's incomplete in that it doesn't automatically chdir back. A proper block-scope "with-" macro will wrap the body in something like:

    char *olddir = getcwd();
    chdir(newdir);
    try {
        do_stuff();
    } finally {
        chdir(olddir);
    }


'try' and 'finally' are in C now? Someone should warn the GCC guys they're behind the times.

Also, getcwd has a size parameter these days, and of course you want to check if the getcwd actually worked.


The OP said: wrap in a macro having similar properties (ala pseudo code).


Good luck writing a macro in C to express language functionality for which you don't have the primitives. It's not exactly lisp. Think of C macros as a way to save you some typing and lisp macros as a way to extend the language.


setjmp and longjmp are sufficient building blocks for this.


you can definitely save the chdir and restore it macro.


Any multithreaded code will be destroyed if relying on chdir. chdir should be deprecated, as resolving relative to is just trivial in an application.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: