1

I am trying to read part of a file and stop and a particular line, using bash. I am not very familiar with bash, but I've been reading the manual and various references, and I don't understand why something like the following does not work (but instead produces a syntax error):

while { read -u 4 line } && (test "$line" != "$header_line")
do
  echo in loop, line=$line
done

I think I could write a loop that tests a "done" variable, and then do my real tests inside the loop and set "done" appropriately, but I am curious as to 1) why the above does not work, and 2) is there some small correction that would make it work? I still fairly confused about when to use [, (, {, or ((, so perhaps some other combination would work, though I have tried several.

(Note: The "read -u 4 line" works fine when I call it above the loop. I have opened a file on file descriptor 4.)

2 Answers 2

5

I think what you want is more like this:

while read -u 4 line && test "$line" != "$header_line"
do
    ...
done

Braces (the {} characters) are used to separate variables from other parts of a string when whitespace cannot be used. For example, echo "${var}x" will print the value of the variable var followed by an x, but echo "$varx" will print the value of the variable varx.

Brackets (the [] characters) are used as a shortcut for the test program. [ is another name for test, but when test detects that it was called with [ it required a ] as its last argument. The point is clarity.

Parenthesis (the () characters) are used in a number of different situations. They generally start subshells, although not always (I'm not really certain in case #3 here):

  1. Retrieving a single exit code from a series of processes, or a single output stream from a sequence of commands. For example, (echo "Hi" ; echo "Bye") | sed -e "s/Hi/Hello/" will print two lines, "Hello" and "Bye". It is the easiest way to get multiple echo statements to produce a single stream.
  2. Evaluating commands as if they were variables: $(expr 1 + 1) will act like a variable, but will produce the value 2.
  3. Performing math: $((5 * 4 / 3 + 2 % 1)) will evaluate like a variable, but will compute the result of that mathematical expression.
Sign up to request clarification or add additional context in comments.

5 Comments

Thank you, thank you! That works well. Concerning {, the bash manual at gnu.org/software/bash/manual/bashref.html#Command-Grouping says that {} can be used to group commands, but does not start a sub-shell like (). That's the way I was trying to use them. Maybe it only works in different context.
I've never used it like that, so I really don't know. I would guess it works like $(cmd args).
No, it doesn't spawn a command. It just groups commands together (like braces do in C-like languages). For example, to sort a file but keep the header at the top: { head -1 file; sed 1d file | sort; } > file.sorted
@glenn Thanks, I was curious about that myself.
As noted in the comments to flolo's answer below, one reason I was having trouble with the { } was that I was missing a ; after the command inside the { }.
2

The && operator is a list operator - he seperates two commands and only executes when the first is true, but in this case the first is the while and he is expecting his do stuff. And then he reaches do and the while stuff is already history. Your intention is to put it into the expression. So you put it together with (). E.g. this a solution with just a small change

while ( read -u 4 line  && test "$line" != "$header_line" )

5 Comments

When I run that, $line is undefined inside the loop. The "(" starts a sub-shell, doesn't it? That might explain it.
Ah in your outer loop is undefined, right. Yes () is a new sub-shell - to run it in the same use { } or you can leave them.
If I wrap the whole thing in { } (replacing the parens with braces) I get a syntax error. I would have expected that to work too, based on the documenation. Anyway, Jonathan has provided a solution that is working. Thank you for your help.
@Paul Lynch: in that case you must be carefull with the syntax, after opening and before closing must be a space, and the interior command must be ended with ;.
Okay, that works too, e.g.: while { read -u 4 line && test "$line" != "$header_line"; }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.