📄 loops.html
字号:
16 exit 0</PRE></TD></TR></TABLE><HR></DIV><P><ANAME="LOOPCS"></A></P><P>It is possible to use <AHREF="commandsub.html#COMMANDSUBREF">command substitution</A> to generate the <TTCLASS="USERINPUT"><B>[list]</B></TT> in a <ICLASS="FIRSTTERM">for loop</I>. See also <AHREF="extmisc.html#EX53">Example 15-52</A>, <AHREF="loops.html#SYMLINKS">Example 10-10</A> and <AHREF="mathc.html#BASE">Example 15-46</A>.</P><DIVCLASS="EXAMPLE"><HR><ANAME="FORLOOPCMD"></A><P><B>Example 10-6. Generating the <TTCLASS="USERINPUT"><B>[list]</B></TT> in a <ICLASS="FIRSTTERM">for</I> loop with command substitution</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # for-loopcmd.sh: for-loop with [list] 3 #+ generated by command substitution. 4 5 NUMBERS="9 7 3 8 37.53" 6 7 for number in `echo $NUMBERS` # for number in 9 7 3 8 37.53 8 do 9 echo -n "$number " 10 done 11 12 echo 13 exit 0</PRE></TD></TR></TABLE><HR></DIV><P>Here is a somewhat more complex example of using command substitution to create the <TTCLASS="USERINPUT"><B>[list]</B></TT>.</P><DIVCLASS="EXAMPLE"><HR><ANAME="BINGREP"></A><P><B>Example 10-7. A <ICLASS="FIRSTTERM">grep</I> replacement for binary files</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # bin-grep.sh: Locates matching strings in a binary file. 3 4 # A "grep" replacement for binary files. 5 # Similar effect to "grep -a" 6 7 E_BADARGS=65 8 E_NOFILE=66 9 10 if [ $# -ne 2 ] 11 then 12 echo "Usage: `basename $0` search_string filename" 13 exit $E_BADARGS 14 fi 15 16 if [ ! -f "$2" ] 17 then 18 echo "File \"$2\" does not exist." 19 exit $E_NOFILE 20 fi 21 22 23 IFS=$'\012' # Per suggestion of Anton Filippov. 24 # was: IFS="\n" 25 for word in $( strings "$2" | grep "$1" ) 26 # The "strings" command lists strings in binary files. 27 # Output then piped to "grep", which tests for desired string. 28 do 29 echo $word 30 done 31 32 # As S.C. points out, lines 23 - 30 could be replaced with the simpler 33 # strings "$2" | grep "$1" | tr -s "$IFS" '[\n*]' 34 35 36 # Try something like "./bin-grep.sh mem /bin/ls" 37 #+ to exercise this script. 38 39 exit 0</PRE></TD></TR></TABLE><HR></DIV><P>More of the same.</P><DIVCLASS="EXAMPLE"><HR><ANAME="USERLIST"></A><P><B>Example 10-8. Listing all users on the system</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # userlist.sh 3 4 PASSWORD_FILE=/etc/passwd 5 n=1 # User number 6 7 for name in $(awk 'BEGIN{FS=":"}{print $1}' < "$PASSWORD_FILE" ) 8 # Field separator = : ^^^^^^ 9 # Print first field ^^^^^^^^ 10 # Get input from password file ^^^^^^^^^^^^^^^^^ 11 do 12 echo "USER #$n = $name" 13 let "n += 1" 14 done 15 16 17 # USER #1 = root 18 # USER #2 = bin 19 # USER #3 = daemon 20 # ... 21 # USER #30 = bozo 22 23 exit 0 24 25 # Exercise: 26 # -------- 27 # How is it that an ordinary user (or a script run by same) 28 #+ can read /etc/passwd? 29 # Isn't this a security hole? Why or why not?</PRE></TD></TR></TABLE><HR></DIV><P>A final example of the <TTCLASS="USERINPUT"><B>[list]</B></TT> resulting from command substitution.</P><DIVCLASS="EXAMPLE"><HR><ANAME="FINDSTRING"></A><P><B>Example 10-9. Checking all the binaries in a directory for authorship</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # findstring.sh: 3 # Find a particular string in the binaries in a specified directory. 4 5 directory=/usr/bin/ 6 fstring="Free Software Foundation" # See which files come from the FSF. 7 8 for file in $( find $directory -type f -name '*' | sort ) 9 do 10 strings -f $file | grep "$fstring" | sed -e "s%$directory%%" 11 # In the "sed" expression, 12 #+ it is necessary to substitute for the normal "/" delimiter 13 #+ because "/" happens to be one of the characters filtered out. 14 # Failure to do so gives an error message (try it). 15 done 16 17 exit 0 18 19 # Exercise (easy): 20 # --------------- 21 # Convert this script to take command-line parameters 22 #+ for $directory and $fstring.</PRE></TD></TR></TABLE><HR></DIV><P><ANAME="LOOPREDIR"></A></P><P>The output of a <ICLASS="FIRSTTERM">for loop</I> may be piped to a command or commands.</P><DIVCLASS="EXAMPLE"><HR><ANAME="SYMLINKS"></A><P><B>Example 10-10. Listing the <ICLASS="FIRSTTERM">symbolic links</I> in a directory</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # symlinks.sh: Lists symbolic links in a directory. 3 4 5 directory=${1-`pwd`} 6 # Defaults to current working directory, 7 #+ if not otherwise specified. 8 # Equivalent to code block below. 9 # ---------------------------------------------------------- 10 # ARGS=1 # Expect one command-line argument. 11 # 12 # if [ $# -ne "$ARGS" ] # If not 1 arg... 13 # then 14 # directory=`pwd` # current working directory 15 # else 16 # directory=$1 17 # fi 18 # ---------------------------------------------------------- 19 20 echo "symbolic links in directory \"$directory\"" 21 22 for file in "$( find $directory -type l )" # -type l = symbolic links 23 do 24 echo "$file" 25 done | sort # Otherwise file list is unsorted. 26 # Strictly speaking, a loop isn't really necessary here, 27 #+ since the output of the "find" command is expanded into a single word. 28 # However, it's easy to understand and illustrative this way. 29 30 # As Dominik 'Aeneas' Schnitzer points out, 31 #+ failing to quote $( find $directory -type l ) 32 #+ will choke on filenames with embedded whitespace. 33 # Even this will only pick up the first field of each argument. 34 35 exit 0 36 37 38 # -------------------------------------------------------- 39 # Jean Helou proposes the following alternative: 40 41 echo "symbolic links in directory \"$directory\"" 42 # Backup of the current IFS. One can never be too cautious. 43 OLDIFS=$IFS 44 IFS=: 45 46 for file in $(find $directory -type l -printf "%p$IFS") 47 do # ^^^^^^^^^^^^^^^^ 48 echo "$file" 49 done|sort 50 51 # And, James "Mike" Conley suggests modifying Helou's code thusly: 52 53 OLDIFS=$IFS 54 IFS='' # Null IFS means no word breaks 55 for file in $( find $directory -type l ) 56 do 57 echo $file 58 done | sort 59 60 # This works in the "pathological" case of a directory name having 61 #+ an embedded colon. 62 # "This also fixes the pathological case of the directory name having 63 #+ a colon (or space in earlier example) as well." 64 </PRE></TD></TR></TABLE><HR></DIV><P>The <TTCLASS="FILENAME">stdout</TT> of a loop may be <AHREF="io-redirection.html#IOREDIRREF">redirected</A> to a file, as this slight modification to the previous example shows.</P><DIVCLASS="EXAMPLE"><HR><ANAME="SYMLINKS2"></A><P><B>Example 10-11. Symbolic links in a directory, saved to a file</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # symlinks.sh: Lists symbolic links in a directory. 3 4 OUTFILE=symlinks.list # save file 5 6 directory=${1-`pwd`} 7 # Defaults to current working directory, 8 #+ if not otherwise specified. 9 10 11 echo "symbolic links in directory \"$directory\"" > "$OUTFILE" 12 echo "---------------------------" >> "$OUTFILE" 13 14 for file in "$( find $directory -type l )" # -type l = symbolic links 15 do 16 echo "$file" 17 done | sort >> "$OUTFILE" # stdout of loop 18 # ^^^^^^^^^^^^^ redirected to save file. 19 20 exit 0</PRE></TD></TR></TABLE><HR></DIV><P><ANAME="LOOPCSTYLE"></A></P><P>There is an alternative syntax to a <ICLASS="FIRSTTERM">for loop</I> that will look very familiar to C programmers. This requires <AHREF="dblparens.html#DBLPARENSREF">double parentheses</A>.</P><DIVCLASS="EXAMPLE"><HR><ANAME="FORLOOPC"></A><P><B>Example 10-12. A C-style <ICLASS="FIRSTTERM">for</I> loop</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # Two ways to count up to 10. 3 4 echo 5 6 # Standard syntax. 7 for a in 1 2 3 4 5 6 7 8 9 10 8 do 9 echo -n "$a " 10 done 11 12 echo; echo 13 14 # +==========================================+ 15 16 # Now, let's do the same, using C-like syntax. 17 18 LIMIT=10 19 20 for ((a=1; a <= LIMIT ; a++)) # Double parentheses, and "LIMIT" with no "$". 21 do 22 echo -n "$a " 23 done # A construct borrowed from 'ksh93'. 24 25 echo; echo 26 27 # +=========================================================================+ 28 29 # Let's use the C "comma operator" to increment two variables simultaneously. 30 31 for ((a=1, b=1; a <= LIMIT ; a++, b++)) # The comma chains together operations. 32 do 33 echo -n "$a-$b " 34 done 35 36 echo; echo 37 38 exit 0</PRE></TD></TR></TABLE><HR></DIV><P>See also <AHREF="arrays.html#QFUNCTION">Example 26-16</A>, <AHREF="arrays.html#TWODIM">Example 26-17</A>, and <AHREF="contributed-scripts.html#COLLATZ">Example A-6</A>.</P><P>---</P><P>Now, a <ICLASS="FIRSTTERM">for loop</I> used in a <SPANCLASS="QUOTE">"real-life"</SPAN> context.</P><DIVCLASS="EXAMPLE"><HR><ANAME="EX24"></A><P><B>Example 10-13. Using <ICLASS="FIRSTTERM">efax</I> in batch mode</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # Faxing (must have 'efax' package installed). 3 4 EXPECTED_ARGS=2 5 E_BADARGS=65 6 MODEM_PORT="/dev/ttyS2" # May be different on your machine. 7 # ^^^^^ PCMCIA modem card default port. 8 9 if [ $# -ne $EXPECTED_ARGS ] 10 # Check for proper number of command line args. 11 then 12 echo "Usage: `basename $0` phone# text-file" 13 exit $E_BADARGS 14 fi 15 16 17 if [ ! -f "$2" ] 18 then 19 echo "File $2 is not a text file." 20 # File is not a regular file, or does not exist. 21 exit $E_BADARGS 22 fi 23 24 25 fax make $2 # Create fax-formatted files from text files. 26 27 for file in $(ls $2.0*) # Concatenate the converted files. 28 # Uses wild card (filename "globbing") 29 #+ in variable list. 30 do 31 fil="$fil $file" 32 done 33 34 efax -d "$MODEM_PORT" -t "T$1" $fil # Finally, do the work. 35 # Trying adding -o1 if above line fails. 36 37 38 # As S.C. points out, the for-loop can be eliminated with 39 # efax -d /dev/ttyS2 -o1 -t "T$1" $2.0* 40 #+ but it's not quite as instructive [grin]. 41 42 exit $? # Also, efax sends diagnostic messages to stdout.</PRE></TD></TR></TABLE><HR></DIV></DD><DT><ANAME="WHILELOOPREF"></A><BCLASS="COMMAND">while</B></DT><DD><P>This construct tests for a condition at the top of a loop, and keeps looping as long as that condition is true (returns a <SPANCLASS="RETURNVALUE">0</SPAN> <AHREF="exit-status.html#EXITSTATUSREF">exit status</A>). In contrast to a <AHREF="loops.html#FORLOOPREF1">for loop</A>, a <ICLASS="FIRSTTERM">while loop</I> finds use in situations where the number of loop repetitions is not known beforehand.</P><P><P><BCLASS="COMMAND">while</B> [<TTCLASS="REPLACEABLE"><I> condition </I></TT>]<BR> do <BR> <TTCLASS="REPLACEABLE"><I>燾ommand(s)</I></TT>... <BR> done </P></P><P>The bracket construct in a <ICLASS="FIRSTTERM">while loop</I> is nothing more than our old friend, the <AHREF="tests.html#TESTCONSTRUCTS1">test brackets</A> used in an <ICLASS="FIRSTTERM">if/then</I> test. In fact, a <ICLASS="FIRSTTERM">while loop</I> can legally use the more versatile <AHREF="tests.html#DBLBRACKETS">double brackets construct</A> (while [[ condition ]]).</P><P><ANAME="WHILENEEDSEMI"></A></P><P><AHREF="loops.html#NEEDSEMICOLON">As is the case with <ICLASS="FIRSTTERM">for loops</I></A>, placing the <ICLASS="FIRSTTERM">do</I> on the same line as the condition test requires a semicolon.</P><P><P><BCLASS="COMMAND">while</B> [<TTCLASS="REPLACEABLE"><I> condition </I></TT>] ; do </P></P><P>Note that the <ICLASS="FIRSTTERM">test brackets</I
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -