09.08.2019»»пятница

Bash While Dead Loop

09.08.2019
    63 - Comments

I'm used to bash's builtin read function in while loops, e.g.:

  1. Bash Read Array While Loop

There are 3 basic loop constructs in Bash scripting, for loop, while loop, and until loop. In this tutorial, we will cover the basics of for loops in Bash as well as the break and continue statements to alter the flow of a loop.

Bash While Dead Loop

I've been working on some make project, and it became prudent to split files and store intermediary results. As a consequence I often end up shredding single lines into variables. While the following example works pretty well,

it's sort of stupid, because the while loop will never run more than once. But without the while,

Bash Read Array While Loop

The read variables are always empty when I use them. I never noticed this behaviour of read, because usually I'd use while loops to process many similar lines. How can I use bash's read builtin without a while loop? Or is there another (or even better) way to read a single line into multiple (!) variables?

Conclusion

The answers teach us, it's a problem of scoping. The statement

is interpreted such that the commands cmd0, cmd1, and cmd4 are executed in the same scope, while the commands cmd2 and cmd3 are each given their own subshell, and consequently different scopes. The original shell is the parent of both subshells.

Scott
7,3755 gold badges29 silver badges53 bronze badges
BananguinBananguin

3 Answers

It's because the part where you use the vars is a new set of commands. Use this instead:

Note that, in this syntax, there must be a space after the {and a ; (semicolon) before the }. Also -n1 is not necessary; read only reads the first line.

For better understanding, this may help you; it does the same as above:

Edit:

It's often said that the next two statements do the same:

Well, not exactly. Fallout 4 planter mod. The first one is a pipe from head to bash's read builtin. One process's stdout to another process's stdin.

The second statement is redirection and process substitution. It is handled by bash itself. It creates a FIFO (named pipe, <(..)) that head's output is connected to, and redirects (<) it to the read process.

So far these seem equivalent. But when working with variables it can matter. In the first one the variables are not set after executing. In the second one they are available in the current environment.

Every shell has another behavior in this situation. See that link for which they are. In bash you can work around that behavior with command grouping {}, process substitution (< <()) or Here strings (<<<).

jordanm
32k3 gold badges89 silver badges98 bronze badges
chaoschaos
36.9k9 gold badges80 silver badges122 bronze badges

To quote from a very useful article wiki.bash-hackers.org:

This is because the commands of the pipe run in subshells that cannot modify the parent shell. As a result, the variables of the parent shell are not modified (see article: Bash and the process tree).

As the answer has been provided a few times now, an alternative way (using non builtin commands..) is this:

geedoubleyageedoubleya

As you noted, the problem was that a pipe to read is run in a subshell.

One answer is to use a heredoc:

This method is nice in that it will behave the same way in any POSIX-like shell.

eradmaneradman

Not the answer you're looking for? Browse other questions tagged bashshell-scriptpipesubshellread or ask your own question.

I have a problem in one of my shell scripts. Asked a few colleagues, but they all just shake their heads (after some scratching), so I've come here for an answer.

According to my understanding the following shell script should print 'Count is 5' as the last line. Except it doesn't. It prints 'Count is 0'. If the 'while read' is replaced with any other kind of loop, it works just fine. Here's the script:

Why does this happen and how can I prevent it? I've tried this in Debian Lenny and Squeeze, same result (i.e. bash 3.2.39 and bash 4.1.5.I fully admit to not being a shell script wizard, so any pointers would be appreciated.

wolfgangszwolfgangsz
7,8592 gold badges22 silver badges31 bronze badges

4 Answers

See argument @ Bash FAQ entry #24: 'I set variables in a loop. Why do they suddenly disappear after the loop terminates? Or, why can't I pipe data to read?' (most recently archived here).

Summary:This is only supported from bash 4.2 and up.You need to use different ways like command substitutions instead of a pipe if you are using bash.

Ignacio Vazquez-AbramsIgnacio Vazquez-Abrams
40k4 gold badges65 silver badges77 bronze badges

This is kind of a 'common' mistake. Pipes create SubShells, so the while read is running on a different shell than your script, that makes your CNT variable never changes (only the one inside the pipe subshell).

Bash while read loop ssh

Group the last echo with the subshell while to fix it (there are many other way to fix it, this is one. Iain and Ignacio's answers have others.)

Long explanation:

  1. You declare CNT on your script to be value 0;
  2. A SubShell is started on the to while read;
  3. Your $CNT variable is exported to the SubShell with value 0;
  4. The SubShell counts and increase the CNT value to 5;
  5. SubShell ends, variables and values are destroyed (they don't get back to the calling process/script).
  6. You echo your original CNT value of 0.
coredumpcoredump
11.3k2 gold badges26 silver badges49 bronze badges

Try passing the data in a sub-shell instead, like it's a file before the while loop. This is similar to lain's solution, but assumes you don't want some intermittent file:

SteveSteve
Loop

Not the answer you're looking for? Browse other questions tagged bashscope or ask your own question.