📄 loops.html
字号:
CLASS="USERINPUT"><B>in [list]</B></TT> part of a <ICLASS="FIRSTTERM">for loop</I> causes the loop to operate on <SPANCLASS="TOKEN">$@</SPAN> -- the list of arguments given on the command line to the script. A particularly clever illustration of this is <AHREF="contributed-scripts.html#PRIMES">Example A-16</A>.</P><DIVCLASS="EXAMPLE"><HR><ANAME="EX23"></A><P><B>Example 10-5. Missing <TTCLASS="USERINPUT"><B>in [list]</B></TT> in a <BCLASS="COMMAND">for</B> loop</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 3 # Invoke this script both with and without arguments, 4 #+ and see what happens. 5 6 for a 7 do 8 echo -n "$a " 9 done 10 11 # The 'in list' missing, therefore the loop operates on '$@' 12 #+ (command-line argument list, including whitespace). 13 14 echo 15 16 exit 0</PRE></TD></TR></TABLE><HR></DIV><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 12-49</A>, <AHREF="loops.html#SYMLINKS">Example 10-10</A> and <AHREF="mathc.html#BASE">Example 12-43</A>.</P><DIVCLASS="EXAMPLE"><HR><ANAME="FORLOOPCMD"></A><P><B>Example 10-6. Generating the [list] in a <BCLASS="COMMAND">for</B> 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 [list].</P><DIVCLASS="EXAMPLE"><HR><ANAME="BINGREP"></A><P><B>Example 10-7. A <AHREF="textproc.html#GREPREF">grep</A> 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" to exercise this script. 37 38 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 [list] 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 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 taking command-line parameters 22 #+ for $directory and $fstring.</PRE></TD></TR></TABLE><HR></DIV><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 <AHREF="external.html#SYMLINKREF">symbolic links</A> 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 # Jean Helou proposes the following alternative: 39 40 echo "symbolic links in directory \"$directory\"" 41 # Backup of the current IFS. One can never be too cautious. 42 OLDIFS=$IFS 43 IFS=: 44 45 for file in $(find $directory -type l -printf "%p$IFS") 46 do # ^^^^^^^^^^^^^^^^ 47 echo "$file" 48 done|sort</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>There is an alternative syntax to a <ICLASS="FIRSTTERM">for loop</I> that will look very familiar to C programmers. This requires double parentheses.</P><DIVCLASS="EXAMPLE"><HR><ANAME="FORLOOPC"></A><P><B>Example 10-12. A C-like <BCLASS="COMMAND">for</B> 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-15</A>, <AHREF="arrays.html#TWODIM">Example 26-16</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 <BCLASS="COMMAND">efax</B> in batch mode</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # Faxing (must have 'fax' installed). 3 4 EXPECTED_ARGS=2 5 E_BADARGS=65 6
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -