2

Here's the code snippet from a shell script. (It's from MPFR library's configure script and it starts with #!/bin/sh. The original script is over 17000 lines long.. It's used when building gcc.)

Because I have so many questions in a short piece of code, I have embedded my questions in the code. Please can somebody explain to me why the code is like this? Also, though I have a vague idea, I would appreciate if someone could explain what this code is doing (I understand it will be difficult because it's only a part of a big script).

if { { ac_try="$ac_link"      
    # <---- question 1 : why is the first curly bracket used for if condition? (probably just for grouping and using the last return code)
    # <---- question 2 : Is this second bracket for locally used code(probably)?
case "(($ac_try" in           # <---- question 3 : what is this "((" symbol?
  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
  *) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5     # <---- question 4 : what is this >&5 redirection? I know >&{1,2,3} but not 5.
  (eval "$ac_link") 2>&5
    # <----- question 5 : why use sub-shell here? not to use eval result?
  ac_status=$?
  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
  test $ac_status = 0; }; then :  # <---- question 6 : is this ':'(nop) here ?
    ....
    some commands
    ....
else
    ....
    some commands
    ....
fi
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Chan Kim
  • 5,177
  • 12
  • 57
  • 112
  • 5
    `configure` scripts are _really_ not meant to be read. They're generated from other sources. As for your question: please remove all the meta-commentary, and one of the rules here is: one question per question. – Mat Feb 09 '15 at 06:10
  • Mat, I understand. This configure script must have come from autoconfigure, thus the variable starting with ac_.. . But to fix a problem, sometimes I have to look into the script and I can solve it what the problem was. So please bear with it for a couple of days. just to see any answers. It'll help others. – Chan Kim Feb 09 '15 at 06:13
  • Read this. Although it won't explain the peculiarities of autogenerated autoconf, which have to do with decades of working around buggy shell implementations. [Posix description of shell](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html) – rici Feb 09 '15 at 06:14
  • rici, thanks, I have "Advanced Bash-Scripting Guide" at hand now but I'll look for cases describing my situation. – Chan Kim Feb 09 '15 at 06:17
  • 1
    @ChanKim: autoconf scripts do not use bash extensions. So you're better off with a basic `sh` manual. Some of the clunky things done in autoconf are precisely because bash extensions aren't available. – rici Feb 09 '15 at 07:27
  • For Q5: the status of the `eval` is used in the next line, `ac_status=$?`. For Q6: the colon command succeeds unless there's a problem evaluating its arguments. In this context, it is indeed a no-op. – Jonathan Leffler Feb 09 '15 at 07:48
  • @JonathanLeffler, by the way why should it use sub-shell for this case? any error message from the previous eval command goes to config.log file and the exit code is available as $?. This part I understand but cannot understand why they use sub-shell here. – Chan Kim Feb 09 '15 at 07:56
  • 1
    Because it is autoconf and it does things inscrutably. I wouldn't bother to work out why; just accept that it does. Understand the overall flow in the script, but don't bother with such details. If you do struggle to find out why, you'll discover that on some archaic system of 10 years ago, under some obscure circumstances, something went wrong if you didn't use the subshell, but using the subshell prevents there being problems, so it was coded thusly. Maybe it was 20 years ago, in fact. If it works, don't worry. If you think it doesn't work, then you'll start running `sh -x configure`. – Jonathan Leffler Feb 09 '15 at 07:58
  • 1
    But, to run `sh -x configure`, you have to be desparate, and you need to capture the output to a file, and you have to be prepared to wade through lots and lots of output. It's probably easier to understand the code from which the script is generated than to understand the generated script. – Jonathan Leffler Feb 09 '15 at 08:00
  • @JonathanLeffler, now I understand it. thanks! – Chan Kim Feb 09 '15 at 08:01

1 Answers1

3

From Bash man page:

{ list; } list is simply executed in the current shell environment. list must be terminated with a newline or semicolon. This is known as a group command. The return status is the exit status of list. Note that unlike the metacharacters ( and ), { and } are reserved words and must occur where a reserved word is permitted to be recognized. Since they do not cause a word break, they must be separated from list by whitespace or another shell metacharacter.

{} is just to list a few commands to run, very much like cmd1; cmd2; cmd3. For example, if you write cmd1 ; cmd2 | cmd3, do you mean {cmd1; cmd2;} | cmd3 or cmd1; {cmd2 | cmd3;}.

{{ }} is just nested command list, easy: e.g. {cmd1; cmd2; {cmd3; cmd4;}; }

For question 3, (( is just in a source string to be matched with the following patterns. If you are asking why it is used, we need possible values of $ac_try to analyze why. Honestly, I don't see many shell scripts purposely adding (( in front of a source string to be matched for patterns.

For question 4, >&5: if file descriptor 5 is not yet created (i.e. mentioned in any part of the script... => be careful, you need to care the scope, some codes runs in sub-shell, which is counted as a sub-shell context/scope), create an unnamed file (well, temp file, if you like), with descriptor 5. This file can be used in other part of the script as an input.

For example, see the part mentioning "exchanges STDIN and STDOUT" in my answer to another question here.

For question 5, the eval, I am not quite sure, just a quick guess (and it depends on what command it evals) by providing you an example why sub-shell makes some differences:

cmd="Foo=1; ls"
(eval $cmd)   # this command runs in sub-shell and thus $Foo in current shell will not be changed.
eval $cmd     # this command runs in current shell and thus $Foo is changed, and it will affect all subsequent commands.

For question 6, look carefully at the man page I mentioned at top of the answer, the {} list syntax, require a final ;. i.e. {cmd1; cmd2 ; } The last ; is required.

--- UPDATE ---

Question 6: Sorry for not seeing the colon... :-) It's no op: see this link.

Community
  • 1
  • 1
Robin Hsu
  • 4,164
  • 3
  • 20
  • 37
  • thanks. now I understand **questions 1, 2** (code block, just use the final return code for if). you should be right for **question 3** "((" but it seems there is a special trick using "((". (I see many of this case "(($" in the configure files. for **question 4**, now I see the first appearance of file descriptor 5 is `exec 5>>config.log { blabla } >&5 ` and for **question 6**, I asked about :(colon) which returns 'true' after 'then'. Any more suggestion or help for **question 5 or 6**? (it's seems like resetting the condition to true) – Chan Kim Feb 09 '15 at 07:47
  • @ChanKim: Given the `exec 5>>config.log`, then anything that redirects to file descriptor 5 is writing to the config log file. The `(eval "$ac_link") 2>&5` sends the errors from the `eval` to the config log file, for example. And for Q5 and Q6, see my comments to the main question. – Jonathan Leffler Feb 09 '15 at 07:51
  • @JonathanLeffler oh, I missed your comment. Thanks!(so my guess was almost right.) – Chan Kim Feb 09 '15 at 07:55
  • If you can post possible values of `$ac_link`, we can analyze why the script needs a sub-shell for the `eval` command. – Robin Hsu Feb 09 '15 at 09:34
  • And sorry for misunderstanding your "((" question. You seems to ask WHY "((" is used, not the syntax of `case ... esac`. But we need possible values of `$ac_try` to analyze why. Honestly, I don't see many shell scripts purposely adding "((" in front of a source string to be searched for patterns. – Robin Hsu Feb 09 '15 at 09:41
  • @RobinHsu, I was analyzing the script. the $ac_link is like '$CC $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' so the script stores some logs in the config.log and evals it (it should eval the $ac_link to expand all the $CC or $CFLAGS variables to their real values. It test compiles the testconf.c file to see if there is any error coming from it. Anyway it is trying the test if the compiler works. In my case, the linker /usr/bin/ld says it can't find crt1.o file. So I have to figure it out now. :) – Chan Kim Feb 09 '15 at 11:48