| Advanced Bash-Scripting Guide: An in-depth exploration of the art of shell scripting | ||
|---|---|---|
| Prev | Chapter 16. I/O Redirection | Next |
Blocks of code, such as while, until, and for loops, even if/then test blocks can also incorporate redirection of stdin. Even a function may use this form of redirection (see Example 23-7). The < operator at the the end of the code block accomplishes this.
Example 16-2. Redirected while loop
1 #!/bin/bash
2
3 if [ -z "$1" ]
4 then
5 Filename=names.data # Default, if no filename specified.
6 else
7 Filename=$1
8 fi
9 # Filename=${1:-names.data}
10 # can replace the above test (parameter substitution).
11
12 count=0
13
14 echo
15
16 while [ "$name" != Smith ] # Why is variable $name in quotes?
17 do
18 read name # Reads from $Filename, rather than stdin.
19 echo $name
20 let "count += 1"
21 done <"$Filename" # Redirects stdin to file $Filename.
22 # ^^^^^^^^^^^^
23
24 echo; echo "$count names read"; echo
25
26 # Note that in some older shell scripting languages,
27 # the redirected loop would run as a subshell.
28 # Therefore, $count would return 0, the initialized value outside the loop.
29 # Bash and ksh avoid starting a subshell whenever possible,
30 # so that this script, for example, runs correctly.
31 # Thanks to Heiner Steven for pointing this out.
32
33 exit 0 |
Example 16-3. Alternate form of redirected while loop
1 #!/bin/bash 2 3 # This is an alternate form of the preceding script. 4 5 # Suggested by Heiner Steven 6 # as a workaround in those situations when a redirect loop 7 # runs as a subshell, and therefore variables inside the loop 8 # do not keep their values upon loop termination. 9 10 11 if [ -z "$1" ] 12 then 13 Filename=names.data # Default, if no filename specified. 14 else 15 Filename=$1 16 fi 17 18 19 exec 3<&0 # Save stdin to file descriptor 3. 20 exec 0<"$Filename" # Redirect standard input. 21 22 count=0 23 echo 24 25 26 while [ "$name" != Smith ] 27 do 28 read name # Reads from redirected stdin ($Filename). 29 echo $name 30 let "count += 1" 31 done <"$Filename" # Loop reads from file $Filename. 32 # ^^^^^^^^^^^^ 33 34 35 exec 0<&3 # Restore old stdin. 36 exec 3<&- # Close temporary fd 3. 37 38 echo; echo "$count names read"; echo 39 40 exit 0 |
Example 16-4. Redirected until loop
1 #!/bin/bash 2 # Same as previous example, but with "until" loop. 3 4 if [ -z "$1" ] 5 then 6 Filename=names.data # Default, if no filename specified. 7 else 8 Filename=$1 9 fi 10 11 # while [ "$name" != Smith ] 12 until [ "$name" = Smith ] # Change != to =. 13 do 14 read name # Reads from $Filename, rather than stdin. 15 echo $name 16 done <"$Filename" # Redirects stdin to file $Filename. 17 # ^^^^^^^^^^^^ 18 19 # Same results as with "while" loop in previous example. 20 21 exit 0 |
Example 16-5. Redirected for loop
1 #!/bin/bash
2
3 if [ -z "$1" ]
4 then
5 Filename=names.data # Default, if no filename specified.
6 else
7 Filename=$1
8 fi
9
10 line_count=`wc $Filename | awk '{ print $1 }'` # Number of lines in target file.
11 # Very contrived and kludgy, nevertheless shows that
12 # it's possible to redirect stdin within a "for" loop...
13 # if you're clever enough.
14 #
15 # More concise is line_count=$(wc < "$Filename")
16
17
18 for name in `seq $line_count` # Recall that "seq" prints sequence of numbers.
19 # while [ "$name" != Smith ] -- more complicated than a "while" loop --
20 do
21 read name # Reads from $Filename, rather than stdin.
22 echo $name
23 if [ "$name" = Smith ] # Need all this extra baggage here.
24 then
25 break
26 fi
27 done <"$Filename" # Redirects stdin to file $Filename.
28 # ^^^^^^^^^^^^
29
30 exit 0 |
We can modify the previous example to also redirect the output of the loop.
Example 16-6. Redirected for loop (both stdin and stdout redirected)
1 #!/bin/bash
2
3 if [ -z "$1" ]
4 then
5 Filename=names.data # Default, if no filename specified.
6 else
7 Filename=$1
8 fi
9
10 Savefile=$Filename.new # Filename to save results in.
11 FinalName=Jonah # Name to terminate "read" on.
12
13 line_count=`wc $Filename | awk '{ print $1 }'` # Number of lines in target file.
14
15
16 for name in `seq $line_count`
17 do
18 read name
19 echo "$name"
20 if [ "$name" = "$FinalName" ]
21 then
22 break
23 fi
24 done < "$Filename" > "$Savefile" # Redirects stdin to file $Filename,
25 # ^^^^^^^^^^^^^^^^^^^^^^^^^^^ and saves it to backup file.
26
27 exit 0 |
Example 16-7. Redirected if/then test
1 #!/bin/bash 2 3 if [ -z "$1" ] 4 then 5 Filename=names.data # Default, if no filename specified. 6 else 7 Filename=$1 8 fi 9 10 TRUE=1 11 12 if [ "$TRUE" ] # if true and if : also work. 13 then 14 read name 15 echo $name 16 fi <"$Filename" 17 # ^^^^^^^^^^^^ 18 19 # Reads only first line of file. 20 # An "if/then" test has no way of iterating unless embedded in a loop. 21 22 exit 0 |
Redirecting the stdout of a code block has the effect of saving its output to a file. See Example 4-2.
![]() | Here documents are a special case of redirected code blocks. |