Unix Shell Programming Third Edition phần 7 pdf

69 255 0
Unix Shell Programming Third Edition phần 7 pdf

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

The set Command The shell's set command is a dual-purpose command: it's used both to set various shell options as well as to reassign the positional parameters $1, $2, and so forth. The -x Option This option turns on trace mode in the shell. It does to the current shell what the command sh -x ctype a did for the execution of the ctype program in Chapter 8, "Decisions, Decisions." From the point that the set -x command is executed, all subsequently executed commands will be printed to standard error by the shell, after filename, variable, and command substitution and I/O redirection have been performed. The traced commands are preceded by plus signs. $ x=* $ set -x Set command trace option $ echo $x + echo add greetings lu rem rolo add greetings lu rem rolo $ cmd=wc + cmd=wc $ ls | $cmd -l + ls + wc -l 5 $ You can turn off trace mode at any time simply by executing set with the +x option: $ set +x + set +x $ ls | wc –l 5 Back to normal $ You should note that the trace option is not passed down to subshells. But you can trace a subshell's execution either by running the shell with the -x option followed by the name of the program to be executed, as in sh -x rolo or you can insert a set -x command inside the file itself. In fact, you can insert any number of set -x and set +x commands inside your program to turn trace mode on and off as desired. set with No Arguments If you don't give any arguments to set, you'll get an alphabetized list of all the variables that exist in your environment, be they local or exported: $ set Show me all variables CDPATH=:/users/steve:/usr/spool EDITOR=/bin/vi HOME=/users/steve IFS= LOGNAME=steve MAIL=/usr/spool/mail/steve MAILCHECK=600 PATH=/bin:/usr/bin:/users/steve/bin:.: PHONEBOOK=/users/steve/phonebook PS1=$ PS2=> PWD=/users/steve/misc SHELL=/usr/bin/sh TERM=xterm TMOUT=0 TZ=EST5EDT cmd=wc x=* $ Using set to Reassign Positional Parameters There is no way to directly assign a value to a positional parameter; for example, 1=100 does not work. These parameters are initially set on execution of the shell program. The only way they may be changed is with the shift or the set commands. If words are given as arguments to set on the command line, those words will be assigned to the positional parameters $1, $2, and so forth. The previous values stored in the positional parameters will be lost forever. So set a b c assigns a to $1, b to $2, and c to $3. $# also gets set to 3. $ set one two three four $ echo $1:$2:$3:$4 one:two:three:four $ echo $# This should be 4 4 $ echo $* What does this reference now? one two three four $ for arg; do echo $arg; done one two three four $ So after execution of the set, everything seems to work consistently: $#, $*, and the for loop without a list. set is often used in this fashion to "parse" data read from a file or the terminal. Here's a program called words that counts the number of words typed on a line (using the shell's definition of a "word"): $ cat words # set $line was executed. After the shell did its substitution, the command line looked like this: set -1 + 5 = 4 When set executed, it saw the - and thought that an option was being selected, thus explaining the error message. Another problem with words occurs if you give it a line consisting entirely of whitespace characters, or if the line is null: $ words Just Enter is pressed CDPATH=.:/users/steve:/usr/spool EDITOR=/bin/vi HOME=/users/steve IFS= LOGNAME=steve MAIL=/usr/spool/mail/steve MAILCHECK=600 PATH=/bin:/usr/bin:/users/steve/bin:.: PHONEBOOK=/users/steve/phonebook PS1=$ PS2=> PWD=/users/steve/misc SHELL=/usr/bin/sh TERM=xterm TMOUT=0 TZ=EST5EDT cmd=wc x=* 0 $ To protect against both of these problems occurring, you can use the option to set. This tells set not to interpret any subsequent arguments on the command line as options. It also prevents set from displaying all your variables if no other arguments follow, as was the case when you typed a null line. So the set command in words should be changed to read set $line With the addition of a while loop and some integer arithmetic, the words program can be easily modified to count the total number of words on standard input, giving you your own version of wc - w: $ cat words # # Count all of the words on standard input # count=0 while read line do set $line count=$(( count + $# )) done echo $count $ After each line is read, the set command is executed to take advantage of the fact that $# will be assigned the number of words on the line. The option is supplied to set just in case any of the lines read begins with a - or consists entirely of whitespace characters. The value of $# is then added into the variable count, and the next line is read. When the loop is exited, the value of count is displayed. This represents the total number of words read. $ words < /etc/passwd 567 $ wc -w < /etc/passwd Check against wc 567 $ (Our version is a lot slower than wc because the latter is written in C.) Here's a quick way to count the number of files in your directory: [1] [1] This technique may not work on very large directories because you may exceed the limit on the length of the command line (the precise length varies between Unix systems). Working with such directories may cause problems when using filename substitution in other commands as well, such as echo * or for file in *. $ set * $ echo $# 8 $ This is much faster than ls | wc -l because the first method uses only shell built-in commands. In general, your shell programs run much faster if you try to get as much done as you can using the shell's built-in commands. Other Options to set set accepts several other options, each of them enabled by preceding the option with a -, and disabled by preceding it with a +. The -x option that we have described here is perhaps the most commonly used. Others are summarized in Table A.9 in Appendix A. The IFS Variable There is a special shell variable called IFS, which stands for Internal Field Separator. The shell uses the value of this variable when parsing input from the read command, output from command substitution (the back-quoting mechanism), and when performing variable substitution. If it's typed on the command line, the shell treats it like a normal whitespace character (that is, as a word delimiter). See what it's set to now: $ echo "$IFS" $ Well, that wasn't very illuminating! To determine the actual characters stored in there, pipe the output from echo into the od (octal dump) command with the -b (byte display) option: $ echo "$IFS" | od –b 0000000 040 011 012 012 0000004 $ The first column of numbers shown is the relative offset from the start of the input. The following numbers are the octal equivalents of the characters read by od. The first such number is 040, which is the ASCII value of the space character. It's followed by 011, the tab character, and then by 012, the newline character. The next character is another newline; this was written by the echo. These characters for IFS come as no surprise; they're the "whitespace" characters we've talked about throughout the book. You can change your IFS to any character or characters you want. This is useful when you want to parse a line of data whose fields aren't delimited by the normal whitespace characters. For example, we noted that the shell normally strips any leading whitespace characters from the beginning of any line that you read with the read command. You can change your IFS to just a newline character before the read is executed, which has the effect of preserving the leading whitespace (because the [...]... signal, all subshells also ignore that signal However, if you specify an action to be taken on receipt of a signal, all subshells will still take the default action on receipt of that signal For the signals we've described, this means that the subshells will be terminated Suppose that you execute the command trap "" 2 and then execute a subshell, which in turn executes other shell programs as subshells If... and then used by the shell Now let's change the IFS to something more visible, like a colon: $ IFS=: $ read x y z 123:345: 678 $ echo $x 123 $ echo $z 678 $ list="one:two:three" $ for x in $list; do echo $x; done one two three $ var=a:b:c $ echo "$var" a:b:c $ Because the IFS was changed to a colon, when the line was read, the shell divided the line into three words: 123, 345, and 678 , which were stored... then generated, it will have no effect on the shells or subshells that are executing because they will all ignore the signal If instead of executing the previous trap command you execute trap : 2 and then execute your subshells, then on receiving the interrupt signal the current shell will do nothing (it will execute the null command), but all active subshells will be terminated (they will take the... and z, respectively In the next to last example, the shell used the IFS when substituting the value of list in the for loop The last example shows that the shell doesn't use the IFS when performing variable assignment Changing the IFS is often done in conjunction with execution of the set command: $ line="Micro Logic Corp.:Box 174 :Hackensack, NJ 076 02" $ IFS=: $ set $line $ echo $# How many parameters... set to be executed whenever signal 0 was received by the shell This signal is generated whenever the shell is exited Because this was set in the login shell, the trap will be taken when you log off The purpose of this trap is to write the time you logged off into the file $HOME/logoffs The command is enclosed in single quotes to prevent the shell from executing date when the trap is defined The trap... the value of pipe Then eval causes it to rescan the line, at which point the | is recognized by the shell as the pipe symbol The eval command is frequently used in shell programs that build up command lines inside one or more variables If the variables contain any characters that must be seen by the shell directly on the command line (that is, not as the result of substitution), eval can be useful... four $ last * zoo_report $ The first time the shell scans echo \$$# Get the last file the backslash tells it to ignore the $ that immediately follows After that, it encounters the special parameter $#, so it substitutes its value on the command line The command now looks like this: echo $4 (the backslash is removed by the shell after the first scan) When the shell rescans this line, it substitutes the... Command If you submit a command line to the background for execution, that command line runs in a subshell independent of your current shell (the job is said to run asynchronously) At times, you may want to wait for the background process (also known as a child process because it's spawned from your current shell the parent) to finish execution before proceeding For example, you may have sent a large sort... the process you want to wait for If omitted, the shell waits for all child processes to complete execution Execution of your current shell will be suspended until the process or processes finish execution You can try the wait command at your terminal: $ sort big-data > sorted_data & Send it to the background [1] 3423 Job number & process id from the shell $ date Do some other work Wed Oct 2 15:05:42... Logic Corp.:Box 174 :Hackensack, NJ 076 02" $ IFS=: $ set $line $ echo $# How many parameters were set? 3 $ for field; do echo $field; done Micro Logic Corp Box 174 Hackensack, NJ 076 02 $ This technique is a powerful one; it uses all built-in shell commands, which also makes it very fast (An alternative approach might have been to echo the value of $line into the tr command, where all colons could have . should note that the trace option is not passed down to subshells. But you can trace a subshell's execution either by running the shell with the -x option followed by the name of the program. -l because the first method uses only shell built-in commands. In general, your shell programs run much faster if you try to get as much done as you can using the shell& apos;s built-in commands. Other. inside IFS and then used by the shell. Now let's change the IFS to something more visible, like a colon: $ IFS=: $ read x y z 123:345: 678 $ echo $x 123 $ echo $z 678 $ list="one:two:three" $

Ngày đăng: 13/08/2014, 15:21

Từ khóa liên quan

Tài liệu cùng người dùng

Tài liệu liên quan