| Advanced Bash-Scripting Guide: A complete guide to shell scripting, using Bash | ||
|---|---|---|
| Prev | Chapter 12. External Filters, Programs and Commands | Next |
-exec COMMAND \;
Carries out COMMAND on each file that find scores a hit on. COMMAND terminates with \; (the ; is escaped to make certain the shell passes it to find literally, which concludes the command sequence). If COMMAND contains {}, then find substitutes the full path name of the selected file.
bash$ find ~/ -name '*.txt' /home/bozo/.kde/share/apps/karm/karmdata.txt /home/bozo/misc/irmeyc.txt /home/bozo/test-scripts/1.txt |
find /home/bozo/projects -mtime 1 # Lists all files in /home/bozo/projects directory tree # that were modified within the last day. |
find /etc -exec grep '[0-9][0-9]*[.][0-9][0-9]*[.][0-9][0-9]*[.][0-9][0-9]*' {} \;
# Finds all IP addresses (xxx.xxx.xxx.xxx) in /etc directory files.
# There a few extraneous hits - how can they be filtered out?
# Perhaps by:
find /etc -type f -exec cat '{}' \; | tr -c '.[:digit:]' '\n' \
| grep '^[^.][^.]*\.[^.][^.]*\.[^.][^.]*\.[^.][^.]*$'
# [:digit:] is one of the character classes
# introduced with the POSIX 1003.2 standard.
# Thanks, S.C. |
![]() | The -exec option to find should not be confused with the exec shell builtin. |
Example 12-2. Badname, eliminate file names in current directory containing bad characters and whitespace.
#!/bin/bash
# Delete filenames in current directory containing bad characters.
for filename in *
do
badname=`echo "$filename" | sed -n /[\+\{\;\"\\\=\?~\(\)\<\>\&\*\|\$]/p`
# Files containing those nasties: + { ; " \ = ? ~ ( ) < > & * | $
rm $badname 2>/dev/null # So error messages deep-sixed.
done
# Now, take care of files containing all manner of whitespace.
find . -name "* *" -exec rm -f {} \;
# The path name of the file that "find" finds replaces the "{}".
# The '\' ensures that the ';' is interpreted literally, as end of command.
exit 0
#---------------------------------------------------------------------
# Commands below this line will not execute because of "exit" command.
# An alternative to the above script:
find . -name '*[+{;"\\=?~()<>&*|$ ]*' -exec rm -f '{}' \;
exit 0
# (Thanks, S.C.) |
See Example 12-20, Example 4-3, and Example 10-8 for scripts using find. Its manpage provides more detail on this complex and powerful command.
A filter for feeding arguments to a command, and also a tool for assembling the commands themselves. It breaks a data stream into small enough chunks for filters and commands to process. Consider it as a powerful replacement for backquotes. In situations where backquotes fail with a too many arguments error, substituting xargs often works. Normally, xargs reads from stdin or from a pipe, but it can also be given the output of a file.
The default command for xargs is echo.
ls | xargs -p -l gzip gzips every file in current directory, one at a time, prompting before each operation.
![]() | An interesting xargs option is -n XX, which limits the number of arguments passed to XX. ls | xargs -n 8 echo lists the files in the current directory in 8 columns. |
![]() | Another useful option is -0, in combination with find -print0 or grep -lZ. This allows handling arguments containing whitespace or quotes. find / -type f -print0 | xargs -0 grep -liwZ GUI | xargs -0 rm -f grep -rliwZ GUI / | xargs -0 rm -f Either of the above will remove any file containing "GUI". (Thanks, S.C.) |
Example 12-3. Log file using xargs to monitor system log
#!/bin/bash # Generates a log file in current directory # from the tail end of /var/log/messages. # Note: /var/log/messages must be world readable # if this script invoked by an ordinary user. # #root chmod 644 /var/log/messages LINES=5 ( date; uname -a ) >>logfile # Time and machine name echo --------------------------------------------------------------------- >>logfile tail -$LINES /var/log/messages | xargs | fmt -s >>logfile echo >>logfile echo >>logfile exit 0 |
Example 12-4. copydir, copying files in current directory to another, using xargs
#!/bin/bash
# Copy (verbose) all files in current directory
# to directory specified on command line.
if [ -z "$1" ] # Exit if no argument given.
then
echo "Usage: `basename $0` directory-to-copy-to"
exit 65
fi
ls . | xargs -i -t cp ./{} $1
# This is the exact equivalent of
# cp * $1
# unless any of the filenames has "whitespace" characters.
exit 0 |
All-purpose expression evaluator: Concatenates and evaluates the arguments according to the operation given (arguments must be separated by spaces). Operations may be arithmetic, comparison, string, or logical.
returns 8
returns 2
Increment a variable, with the same effect as let y=y+1 and y=$(($y+1)) This is an example of arithmetic expansion.
External programs, such as sed and Perl have far superior string parsing facilities, and it might well be advisable to use these rather than the built-in Bash ones.
Example 12-5. Using expr
#!/bin/bash # Demonstrating some of the uses of 'expr' # ======================================= echo # Arithmetic Operators echo "Arithmetic Operators" echo a=`expr 5 + 3` echo "5 + 3 = $a" a=`expr $a + 1` echo echo "a + 1 = $a" echo "(incrementing a variable)" a=`expr 5 % 3` # modulo echo echo "5 mod 3 = $a" echo echo # Logical Operators echo "Logical Operators" echo a=3 echo "a = $a" b=`expr $a \> 10` echo 'b=`expr $a \> 10`, therefore...' echo "If a > 10, b = 0 (false)" echo "b = $b" b=`expr $a \< 10` echo "If a < 10, b = 1 (true)" echo "b = $b" echo echo # Comparison Operators echo "Comparison Operators" echo a=zipper echo "a is $a" if [ `expr $a = snap` ] # Force re-evaluation of variable 'a' then echo "a is not zipper" fi echo echo # String Operators echo "String Operators" echo a=1234zipper43231 echo "The string being operated upon is \"$a\"." # index: position of substring b=`expr index $a 23` echo "Numerical position of first \"23\" in \"$a\" is \"$b\"." # substr: print substring, starting position & length specified b=`expr substr $a 2 6` echo "Substring of \"$a\", starting at position 2, and 6 chars long is \"$b\"." # length: length of string b=`expr length $a` echo "Length of \"$a\" is $b." # 'match' operations similarly to 'grep' # uses Regular expressions b=`expr match "$a" '[0-9]*'` echo Number of digits at the beginning of \"$a\" is $b. b=`expr match "$a" '\([0-9]*\)'` # Note escaped parentheses. echo "The digits at the beginning of \"$a\" are \"$b\"." echo exit 0 |
![]() | The : operator can substitute for match. For example, b=`expr $a : [0-9]*` is the exact equivalent of b=`expr match $a [0-9]*` in the above listing.
|