Extending the Power of Darwin

Một phần của tài liệu Mac OS x leopard (Trang 355 - 626)

1: #!/bin/sh 2:

3: # togglevis

4: # A shell script to toggle the visibility of hidden files in the Finder 5:

6: set `ps acx | grep Finder`

7:

8: show=`defaults read com.apple.finder AppleShowAllFiles`

9:

10: if [ $show -eq 1 ]; then

11: defaults write com.apple.finder AppleShowAllFiles 0 12: kill $1

13: else

14: defaults write com.apple.finder AppleShowAllFiles 1 15: kill $1

16: fi

NOTE For those of you who really hate line numbers in programs because they prevent you from copying the text out of an e-book and running it, later in this chapter there will be a Perl script that you should be able to tweak (when you are done with this chapter) to strip them out automatically.

This script will toggle the visibility of hidden items in the Mac OS X Finder, which is done by setting the hidden Finder preference AppleShowAllFiles to 1 (to show all files) or 0 (to hide hidden files) and then restarting the Finder to immediately enact the change.

NOTE Truth be told, this script doesn’t exactly restart the Finder. The script instead just kills the Finder. OS X, depending on the Finder, notices this and restarts the Finder for you.

The first line, #!/bin/sh, often referred to as the interpreter line, is important for all scripts because it points the shell to the executing script interpreter.

NOTE On Leopard, /bin/shand /bin/bashare both Bash shell executables. Most shell scripts in OS X (and generally in Unix) are written for the Bourne shell (sh) for compatibility across var- ious Unix platforms (of which almost all have the Bourne shell installed). The Bash shell itself (a.k.a. Bourne Again SHell) is 100 percent backward compatible with the Bourne shell, with some extra features borrowed from the C shell and Korn shell. The general scripting syntax we are covering in this chapter is mostly Bourne shell compatible; however, there may be certain commands issued inside a script that are specific to OS X.

Lines 3 and 4, like all script lines beginning with #(except the sh-bang line), are comments.

These are included to provide a clue about what’s going on for anyone reading the script. Com- ments have no effect on how the script runs and in this respect are purely optional elements. It is a good habit to use comments, not only for others who may be looking at your script but also for yourself should you have to look at a script weeks, months, or even years after you wrote it.

Here the first comment line gives the name of the script, and the second comment describes what the script does.

Line 6 shows us two things, the use of the setcommand and the use of backticks (`) in scripts. In a shell script, anything within backticks is executed, and the result is passed to the script (this is called command substitution). The setcommand sets the arguments used in the script just as if they were entered at the command line. So, line 6 takes the results from the ps acx

| grep Findercommand (which would be something like 261 ?? S 0:00.93 Finder) and makes them our arguments (of which there would be 5). The importance of line 6 for the rest of the script is that it specifically assigns the PID of the Finder to our first argument that can then be accessed as the variable $1.

Line 8 sets the variable showto the result of the defaults read com.apple.finder AppleShowAllFiles command. defaultsprovides a way to access and alter Mac OS X user default settings from the command line. In this case, we use it to read the AppleShowAllFiles value stored in the com.apple.finder preference to determine the existing setting (0 or 1) so we can then toggle the setting. If the AppleShowAllFiles value doesn’t exist yet in your Finder preference file, you will get an error similar to this:

2007-06-26 12:24:00.175 defaults[1180]

The domain/default pair of (com.apple.finder, AppleShowAllFiles) does not exist togglevis.sh: line 10: [: ==: unary operator expected

In this case you can ignore this, since the script will continue and actually create this value.

However, if you continue to get errors after this (or if you get a vastly different error), then some- thing else is wrong, and you should check your script closely.

Lines 10 through 16 provide a conditional if...then...elsestatement and provide the expected actions based on the condition. First, in line 10 we check to see whether the show vari- able (which is the set value of com.apple.finder AppleShowAllFiles) is 1; if it is, then we move on to line 11 where we change com.apple.finder AppleShowAllFiles to 0. Then in line 12 we kill the Finder using $1, which we set in line 6. If the condition in line 10 is false (the show variable is not 1), then we move on to the else part of the script, and in line 14 we set com.apple.finder AppleShowAllFiles to 1 and then kill the Finder. Line 16 ends the if...then...elsestatement with fi(that’s ifbackward).

The script ends when it runs out of things to do.

There are a few ways to run this script: one is to pass the script to the shell executable as an argument, and the other is to make the actual script executable. Passing the script as an argu- ment to the shell looks like this:

leopard:~/bin scott$ sh togglevis

If you want to have access to the script easily from anywhere, though, you must first make the script executable and make sure it’s in a directory that is included in your $PATH(as discussed in the previous chapter). For example, we’ve placed this file in our own bindirectory, which you can see is set in our path with the following statement:

leopard:~/bin scott$ echo $PATH

/opt/local/bin:/Users/scott/bin:/usr/local/bin:/bin:/sbin:/usr/bin:/usr/sbin To make it executable, we use the chmodcommand:

leopard:~/bin scott$ chmod u+x togglevis

Now provided the script is in your path, you can use togglevisas you would any command from the command line.

Variables

Variables in shell scripts are generally reserved for use from within the script itself unless they are explicitly exported as environmental variables. Environmental variables (which include things like $PATH) are variables that are available to all shell scripts and programs, where shell variables are available only within your script.

Setting a variable is quite easy; just create a variable name, and set its value like this:

aVariable=Value

CHAPTER 19EXTENDING THE POWER OF DARWIN 331

NOTE The possible names a variable can have are virtually endless, but there are some rules.

First, although the name can contain letters, numbers, or underscores, it’s best that they begin with a letter (and they can’t begin with a number, since that is reserved for command-line argu- ment variables). Second, variables obviously can’t share a name with a reserved word, which is one of the many words that have special meaning to the shell. Finally, by convention, environ- mental variables are often written with all capital letters, and shell variables are traditionally written in all lowercase letters, but of late many people use camel notation, which starts out lowercase but uses a capital letter where a new word begins. An example of camel notation is aVeryLongVariable.

It is important not to include spaces around the =when declaring your variables; if you do, you will get an error.

If you want to make your variable an environmental variable, then you must export it using the exportcommand. Usually this is done immediately after the variable is declared, which can be done all on one line like this:

ENVVAR=Value; export ENVVAR

After the shell exports the variable, it will be immediately available for any program or shell script until the parent shell process exits. (In other words, every time you launch a new shell, you will need to redeclare and export the variable to use it again.) If you are planning on using an environmental variable over and over, it may be best to declare it in your .bash_profilefile so it gets declared each time you launch your terminal or a shell.

Argument Variables

Some of the most commonly used variables are ones you don’t need to declare at all. These are variables passed to the script as arguments from the command line when the script is called. For example, if you were to run an executable script called ascript, you could pass arguments to the script when you call it, like this:

leopard:~ scott$ ascript arg1 arg2 arg3

Here the arg1, arg2, and arg3values are automatically assigned to variables $1, $2, and $3so they can be used as needed in the script.

NOTE In many computer languages, counting begins with 0, not 1. Command-line arguments in scripts are no exception. In scripts, $0is always the script itself and will return the complete path name of the executed script.

These special variables don’t have to be passed in from the command line, though. Occa- sionally it may be advantageous to create and control these arguments from within the script.

This can be done by using the setcommand (as we did in our example shell script earlier). The setcommand will replace all command-line arguments with the arguments provided.

NOTE setis a fairly complex command in Bash with lots of different capabilities. If you use setalone on the command line, it will list every environmental variable and function available to you in a nice, readable format. Used with options, setallows you to toggle many shell attributes. If you are interested, the gory details of all the possibilities for this command begin on page 51 of the Bash main page and continue until about three-quarters of the way through page 53. Also of note, setbehaves differently in other shells; for example, in cshyou would use setin place of exportto create environmental variables.

Command Substitution

Another way to assign a variable is through command substitution. This allows you to work with the result of any shell command by enclosing the command with backticks (the ` character below the Esc key on most Mac keyboards). This allows you to take advantage of any shell com- mand, making up for most of the shell’s natural shortcomings. For example, the Bourne shell doesn’t have built-in capabilities for simple arithmetic; however, using command substitution, you can take advantage of command-line executables that do this for you anyway, like this:

#!/bin/sh x=2 y=3

z=`expr $x + $y`

echo $z

This little script uses the exprexecutable to do the math for you and then prints 5, which is what you’d expect.

NOTE The Bash shell actually does have built-in arithmetic capability, so if you were so inclined, you could replace the z=`expr $x + $y`line with let z=$x+$yand get the same results.

Controlling the Flow

The ability to control the flow of a script makes it much more adaptable and useful. There are two primary ways to control the flow of a script: conditionals, which will execute commands if certain conditions are met, and loops, which can repeat commands over and over a predeter- mined number of times.

Conditional Statements

There are two common conditional statements available in shell scripts: ifstatements and case statements. An ifstatement checks to see whether a condition is true or false. If the condition is true, then a block of code is run; if it is false, an elseblock is run. The whole thing looks some- thing like this:

if [ condition ] then

condition is met block else

condition not met block fi

The fiat the end signals the end of the ifstatement.

One other thing that can be added to respond to multiple conditions is the elifcondition.

This allows you to create logic like this:

if [ condition1 ] then

condition1 block elif [ condition2 ]

condition2 block elif [ condition3 ] ...

else

no conditions met block fi

CHAPTER 19EXTENDING THE POWER OF DARWIN 333

One thing to keep in mind is that once the condition is met, the script will execute that block and then exit the ifblock entirely, so if condition1is met, the script will not check for condition2 or run the code in that block.

There are a number of ways to create conditional statements; one of the most common is to use a mathematical test condition as listed in Table 19-1 (the Bash alternate expressions will work only with Bash and not a traditional Bourne shell).

Table 19-1.Mathematic Test Expressions for sh and Bash Expression (sh) Bash Alternate Result

$x -eq $y $x == $y True if $xis equal to $y

$x -ne $y $x != $y True if $xis not equal to $y

$x -lt $y $x < $y True if $xis less than $y

$x -gt $y $x > $y True if $xis greater than $y

$x -le $y $x <= $y True if $xis less than or equal to $y

$x -ge $y $x >= $y True if $xis greater than or equal to $y

Another common conditional statement is to check on the existence of a file or directory.

This is especially handy if you are creating workflow or other scripts that interact with the file system. An ifstatement that checks for the existence of a particular file would look like this:

if [ -e /path/to/file ] then

If file exists do this else

If file doesn't exist do this fi

Here the -eoption checks for the existence of the file. Table 19-2 lists some possible options.

Table 19-2.Common Options for Testing File Attributes in Shell Scripts Option Result

-e file True if the file exists at all

-f file True if file exists and it is a regular file -d file True if file exists and it is a directory -r file True if file exists and it is readable -w file True if file exists and it is writeable -x file True if file exists and it is executable

It is also possible to test multiple conditions using logical and/or statements. This allows you to check either whether multiple statements are all true or whether one of many statements is true. This is done using either &&or ||(that’s the long bar over the \ key) between the conditions.

[ condition1 ] && [ condition2 ]will return true if both conditions are true, while [ condition1 ]

|| [ condition2 ]will return true if either condition is true.

One final common conditional is to utilize the exit status of a command. Every command you run will provide an exit status; that’s usually a 0 if everything went smoothly and some other number if something went wrong. ifknows about this, so you can use statements like this:

if command then

if command exits normally do this else

if command resulted in an error do this fi

Another way of dealing with multiple potential conditions is the use of the casestatement.

A casestatement looks at a variable and responds differently depending on its value.

A casestatement is generally much easier to use then an ifstatement; however, it’s a viable alternate only if you are dealing with multiple similar outcomes that may be stored in a singe variable. A casestatement looks like this:

case "$caseVar" in

1) command(s) to run if the value of $caseVar is "1" ;;

2) command(s) to run if the value of $caseVar is "2" ;;

dog) command(s) to run if the value of $caseVar is "dog" ;;

*) command(s) to run if none of the previous cases matched, "*" is a wildcard ;;

esac

Loops

Now that you’ve learned a bit about how to selectively run or not run a command based on a condition, we will cover how you can run a command over and over with loops. There are three main types of loops for shell scripting: while, until, and forloops.

The whileand untilloops are similar in idea, but they do the opposite in practice. Each of them takes a condition (similar to the ifstatement); however, where the whileloop will run a block of code as long as the condition is true, the untilloop will run as long as the condition is false.

CAUTION Poorly written loops can have the adverse effect of running forever. If you are run- ning a script from the command line, it’s easy enough to stop such a runaway script with Ctrl+C, but if this is part of a background or start-up script, things can get more complicated.

Therefore, it’s a good idea to test your scripts from the command line before you place them in start-up files and such.

A simple whileloop could look like this:

#!/bin/sh x=1

while [ $x -le 10 ] do

echo $x x=`expr $x + 1`

done

This script will simply go through the whileloop, printing the value of $xand then increas- ing the value of xby 1 as long as the value of $xis equal to or less than 10. If we switched while to until, the loop would skip entirely since our declared value of x(1) would immediately be less than or equal to 10.

NOTE Like ifstatements, whileand untilloops can also evaluate multiple conditions using logical and (&&) and or (||) statements.

CHAPTER 19EXTENDING THE POWER OF DARWIN 335

The other common loop is the forloop. Rather than relying on a true/false condition like the whileand untilloops, a forloop iterates over all the elements in a list. For example, a simple script to echo back the command-line arguments could look like this:

#!/bin/sh

echo "The command line arguments given to $0 are: "

for x in $@

do

echo $x done

NOTE $@is a special variable that contains each command-line argument together in a single variable.

You may find that there is situation where you need to get out of a loop before it completes, which is accomplished with either the break or continuecommand. The breakcommand will immediately stop the current loop pass and exit the loop process entirely (that is, the script will continue immediately following the loop). The continuecommand will cause a halt in the current pass through the loop but will continue the loop process if the conditions are still met.

Input and Output

Many scripts you write will need to provide output, and often you may want a script to prompt for input as well. In the previous chapter, you learned most of the basics you need in order to output text either to the terminal or to a file using the echocommand (with redirection if you want to write the output to a file), but there is also the printfcommand that provides more options in how your output is presented. To get information into a script, you can use the read command, which will provide a prompt at the command line for input.

The following script (enhanced from previously) shows how readand printfwork (line numbers have been added for reference):

1: #!/bin/sh 2:

3: printf "Please enter your input here: "

4: read input 5: set $input 6: c=$#

7: printf "You entered The following: "

8: for x in $@

9: do

10: c=`expr $c - 1`

11: if [ $c -gt 0 ] 12: then

13: printf "\"$x\", "

14: else

15: printf "and \"$x\".\n"

16: fi 17: done

When you run this script, you get something along the lines of this:

Please enter your input here: hello goodbye dogcow

You entered The following: "hello", "goodbye", and "dogcow".

Một phần của tài liệu Mac OS x leopard (Trang 355 - 626)

Tải bản đầy đủ (PDF)

(626 trang)