-->
Page 362
There is no need to specify filenames to xargs because it reads these from the standard input, so the xargs command will be
xargs grep -l foo
To get a list of files for xargs to give to grep, use find to list the files in /usr/src/linux that end in .c:
find /usr/src/linux -name "*.c"
Then attach the standard output of find to the standard input of xargs with a pipe:
find /usr/src/linux -name "*.c" | xargs grep -l foo
Finally, tack on a wc -l to get a count and you have
find /usr/src/linux -name "*.c" | xargs grep -l foo | wc -l
On my system this took about 29 seconds, which is considerably faster. The difference becomes even greater when more complex commands are run and the list of files is longer.
You need to be careful about filenames that contain spaces in them. Many people believe that spaces are not valid characters in UNIX filenames, but they are, and handling them correctly is becoming an issue of greater importance because today many machines are able to mount and read disks from systems that frequently use spaces in filenames.
I routinely mount Mac HFS disks (Zip, floppy, hard disk) and many files on these disks have spaces in their filenames. This will confuse xargs because it uses the newline character (\n) and the space character as filename delimiters. The GNU version of xargs provides a pretty good workaround for this problem with the --null and -0 options, which tell xargs to use the null character (\0 or \000) as the delimiter. In order to generate filenames that end in null, find can be given the -print0 option instead of -print.
As an illustration, here is an ls of my Mac's Zip Tools disk:
./ .resource/ Desktop DB* System Folder/ ../ .rootinfo Desktop DF* Utilities/ .finderinfo/ Applications/ Icon:0d*
Notice that the Desktop DB and the Desktop DF files have spaces in them, as does the System Folder directory. If I wanted to run through this Zip disk and remove all files that end in prefs copy, I would normally try
find /mnt/zip -name "*prefs copy" -print | xargs rm
However, this won't work because I have a filename with spaces, but if I add -print0, I can do this with no problems:
find /mnt/zip -name "*prefs copy" -print0 | xargs rm
Two other options that are useful for xargs are the -p option, which makes xargs interactive, and the -n args option, which makes xargs run the specified command with only args number of arguments.
Page 363
Some people wonder why the -p option. The reason is that xargs runs the specified command on the filenames from its standard input, so interactive commands like cp -i, mv -i, and rm -i don't work right. The -p option solves that problem. In the preceding example, the -p option would have made the command safe because I could answer yes or no to each file. So the real command I typed was the following:
find /mnt/zip -name "*prefs copy" -print0 | xargs -p rm
Many users frequently ask why xargs should be used when shell command substitution archives the same results. The real drawback with commands such as
grep -l foo `find /usr/src/linux -name "*.c"`
is that if the set of files returned by find is longer than the system's command-line length limit, the command will fail. The xargs approach gets around this problem because xargs runs the command as many times as is required, instead of just once.
The GNU shell utilities are a package of small shell programming utilities. The following programs are included in the package:
basename | printenv |
chroot | printf |
date | pwd |
dirname | seq |
echo | sleep |
env | stty |
expr | su |
factor | tee |
false | test |
groups | true |
hostname | tty |
id | uname |
logname | users |
nice | who |
nohup | whoami |
pathchk | yes |
One of the first things many people do when they log on to a new machine is to see who else is logged on. The GNU shell utilities provide the commands who and users to give
Page 364
information about which users are currently logged on and what they are doing. The users command just prints out a list of names of people who are logged on. The who command is much more sophisticated.
In addition to giving information about who is logged on, who makes it possible to find out how long people have been idle, when they logged on, and if they are allowing other people to talk. Some of the options that who recognizes are as follows:
-H or --heading | Prints a heading |
-T, -w, or--mesg | Adds user message status as +, -, or ? |
-i, -u, or --idle | Adds user idle time as HOURS:MINUTES, . (less than a minute), or old (greater than a day) |
One of the useful features of who over users is that because who outputs one user entry per line, it can be used with commands like grep or sed to process and format its output with ease.
The next set of frequently used commands are the id commands.
Knowing your own uid and gid and having the ability to determine other users' uids and gids are very handy skills.
Almost all users know about the commands whoami and groups, but many have never heard of id, which encompasses their functionality and adds the ability to determine user information about other people on the system.
By default, the id command prints out all the identification information about the current user. When I run id on my system, I get (as myself)
uid=500(ranga) gid=100(users) groups=100(users)
But, I can also run id on my brother's login name:
id vathsa
and I get
uid=501(vathsa) gid=100(users) groups=500(vathsa)
I could have determined all the preceding information by looking in /etc/passwd, but id is a much easier way of accomplishing the task.
In addition, the output of id can be tailored using the following options:
-g or --group | Prints only the group ID |
-G or --groups | Prints only the supplementary groups |
-n or --name | Prints a name instead of a number |