Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
The Ultimate Linux Shell Scripting Guide

You're reading from   The Ultimate Linux Shell Scripting Guide Automate, Optimize, and Empower tasks with Linux Shell Scripting

Arrow left icon
Product type Paperback
Published in Oct 2024
Publisher Packt
ISBN-13 9781835463574
Length 696 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Donald A. Tevault Donald A. Tevault
Author Profile Icon Donald A. Tevault
Donald A. Tevault
Arrow right icon
View More author details
Toc

Table of Contents (26) Chapters Close

Preface 1. Getting Started with the Shell 2. Interpreting Commands FREE CHAPTER 3. Understanding Variables and Pipelines 4. Understanding Input/Output Redirection 5. Customizing the Environment 6. Text-Stream Filters – Part 1 7. Text Stream Filters – Part 2 8. Basic Shell Script Construction 9. Filtering Text with grep, sed, and Regular Expressions 10. Understanding Functions 11. Performing Mathematical Operations 12. Automating Scripts with here Documents and expect 13. Scripting with ImageMagick 14. Using awk – Part 1 15. Using awk – Part 2 16. Creating User Interfaces with yad, dialog, and xdialog 17. Using Shell Script Options with getops 18. Shell Scripting for Security Professionals 19. Shell Script Portability 20. Shell Script Security 21. Debugging Shell Scripts 22. Introduction to Z Shell Scripting 23. Using PowerShell on Linux 24. Other Books You May Enjoy
25. Index

Executing Multiple Commands at Once

From either the command-line or from within shell scripts, it’s handy to know how to combine multiple commands into one single command. In this section, I’ll demonstrate three ways to do that which are:

  • Running commands interactively
  • Using command sequences
  • Using the find utility

Running Commands Interactively

This is a form of shell-script programming, except that you’re just executing all commands from the command-line, instead of actually writing, saving, and executing a script. Here, you are creating a for loop – with each command of the loop on its own separate line – to perform a directory listing three times.

[donnie@fedora ~]$ for var in arg1 arg2 arg3
> do
> echo $var
> ls
> done
. . .
. . .
[donnie@fedora ~]$

At the end of each line, you’ll hit the Enter key. But, nothing will happen until you type the done command on the final line. The for loop will then run three times, once for each of the three listed arguments. Each time that it runs, the value of an argument gets assigned to the var variable, and the echo command prints the currently-assigned value. The output will look something like this:

arg1
 4-2_Building_an_Alpine_Container.bak     Public
 4-2_Building_an_Alpine_Container.pptx    pwsafe.key
arg2
 4-2_Building_an_Alpine_Container.bak     Public
 4-2_Building_an_Alpine_Container.pptx    pwsafe.key
arg3
 4-2_Building_an_Alpine_Container.bak     Public
 4-2_Building_an_Alpine_Container.pptx    pwsafe.key

Next, hit the up arrow key on your keyboard, and you’ll see the for loop that you just executed If you try this with bash, you’ll see that the individual commands are separated by semi-colons, like so:

[donnie@fedora ~]$ for var in arg1 arg2 arg3; do echo $var; ls; done

On zsh, pressing the up arrow key will cause the command components to appear on their own separate lines, as you see here:

donnie@opensuse:~> for var in arg1 arg2 arg3
do
echo $var
ls
done

Either way, the for loop will run again when you hit the Enter key.

If you’re still a bit unclear about how for loops work, have no fear. We’ll look at them in greater detail once we start actually creating shell scripts.

Using Command Sequences

Command sequences are another type of programming structure that you’ll find very useful. Here, I’m demonstrating how to use them from the command-line so that you can grasp the basic concepts. In the upcoming chapters, I’ll show you examples of how to use them in shell scripts.

Chaining Commands with a Semi-Colon

You can also use the semi-colon to separate stand-alone commands that you want to execute from the same command entry. If you wanted to cd to a certain directory and then look at its contents, you could enter each command on its own line. Or, you could enter them both on the same line. This process is called command chaining, which looks like this:

[donnie@fedora ~]$ cd /var ; ls
account  cache  db     ftp    kerberos  local  log   nis  preserve  spool  yp
adm      crash  empty  games  lib       lock   mail  opt  run       tmp
[donnie@fedora var]$
[donnie@fedora ~]$ cd /far ; ls
bash: cd: /far: No such file or directory
 4-2_Building_an_Alpine_Container.bak     Public
 4-2_Building_an_Alpine_Container.pptx    pwsafe.key
 addresses.txt                                                 python_container
 alma9_default.txt                                         rad-bfgminer
. . .
. . .
[donnie@fedora ~]$

The first command failed because I tried to cd into a non-existent directory. But, the second command still executed, which listed the files in my home directory.

Conditional Command Execution with Double Ampersands

You can also instruct bash or zsh to only execute the second command if the first command successfully completes. Just separate the commands with && instead of with a semi-colon, like this:

[donnie@fedora ~]$ cd /var && ls
account  cache  db     ftp    kerberos  local  log   nis  preserve  spool  yp
adm      crash  empty  games  lib       lock   mail  opt  run       tmp
[donnie@fedora var]$

What if the first command doesn’t run successfully? Note here that the second command doesn’t execute:

[donnie@fedora ~]$ cd /far && ls
bash: cd: /far: No such file or directory
[donnie@fedora ~]$

Conditional Command Execution with Double Pipes

If you want bash or zsh to execute the second command only if the first command doesn’t run successfully, just separate the commands with ||. (This is a pair of pipe characters, which you’ll find on the same key as the backslash.) To illustrate, let’s again make a slight typo while trying to change directories.

[donnie@fedora ~]$ ce /var || echo "This command didn't work."
bash: ce: command not found
This command didn't work.
[donnie@fedora ~]$
[donnie@fedora ~]$ cd /var || echo "This command didn't work."
[donnie@fedora var]$

For a more practical example, try changing to a directory, creating it if it doesn’t exist, and then changing to it after it’s been successfully created.

[donnie@fedora ~]$ cd mydirectory || mkdir mydirectory && cd mydirectory
bash: cd: mydirectory: No such file or directory
[donnie@fedora mydirectory]$

You’ll still get an error message saying that the directory you tried to access doesn’t exist. But, look at the command prompt, and you’ll see that the directory has been created, and that you’re now in it.

Using the find Utility

We’ll now take a short intermission from our discussion of running multiple commands in order to introduce the find utility, which is truly the Cool-Mac Daddy of all search utilities. After this introduction, I’ll use find to show you more ways to run multiple commands at once.

Also, it would behoove us to mention that find isn’t just good for command-line searches. It’s also excellent for use within shell scripts, as you’ll see much later.

If you’re as old as I am, you might remember the Windows XP search pooch, which pranced around on your screen every time you did a file search from the Windows XP graphical search utility. It was cute, but it didn’t add to your search power. With the Linux find utility, you can perform powerful searches on just about any criterion you can think of, and then--from the same command-line entry--invoke another utility to do whatever you need to do with the search results. I won’t try to discuss every option there is for find, since there are so many. Rather, I’ll give you an overview of what you can do with find, and let you read its man page for the rest. (Just enter man find at the command-line to read about all of its options.)

In order to perform the most basic of searches, you’ll need to specify two things:

  • The search path: You can perform a search in either a specific path, or the entire filesystem. Since find is inherently recursive, the search will automatically extend to all of the subdirectories that are beneath of the directory that you specify. (Of course, you can also add command switches that limit the depth of the search.)
  • What you’re searching for: There are a lot of ways that you can specify this. You can search for files of a specific name, and decide whether to make the search case-sensitive. You can also use wildcards, or search for files with certain characteristics or that are of a certain age. Or, you can combine multiple criteria for even more specific searches. The main thing that limits you is your own imagination.

So now, let’s say that you want to search the entire filesystem for all files whose names end in .conf. You’ll want to use either the -name or the -iname switch in front of the file description that you want to search for. Otherwise, you’ll get a jumbled up mess of every directory listing that you’ve searched, with the information you’re looking for mixed in. For case-sensitive searches, use -name, and for case-insensitive searches, use -iname. In this case, we’ll use -iname, since we want to make the search case-insensitive.

I know, I’ve told you previously that most whole-word option switches are preceded by a pair of dashes. The find utility is an exception to the rule, because its whole-word option switches are preceded by only a single dash.

Also, be aware that searching through an entire filesystem on a production server with very large drives can take a long time. It’s sometimes necessary to do that, but it’s best to confine your searches to specific directories whenever possible.

If you include a wildcard character with a search criterion, you’ll need to enclose that search criterion in quotes. That will keep the shell from interpreting the wildcard character as an ambiguous file reference. For example, to perform a case-insensitive search through the current working directory and all of its subdirectories for all files with names ending with a .conf filename extension, I would do this:

[donnie@fedora ~]$ find -iname '*.conf'
./.cache/containers/short-name-aliases.conf
./.config/lxsession/LXDE/desktop.conf
./.config/pcmanfm/LXDE/desktop-items-0.conf
./.config/pcmanfm/LXDE/pcmanfm.conf
./.config/lxterminal/lxterminal.conf
./.config/Trolltech.conf
. . .
. . .
./tor-browser/Browser/TorBrowser/Data/fontconfig/fonts.conf
./rad-bfgminer/example.conf
./rad-bfgminer/knc-asic/RPi_system/raspi-blacklist.conf
./something.CONF
[donnie@fedora ~]$[donnie@fedora ~]$

By using the -iname option, I was able to find files with names that ended in either .conf or .CONF. If I had used the -name option instead, I would only have found files with names that end in .conf.

Normally, you would specify the search path as the first component of the find command. In the GNU implementation of find that’s included on Linux-based operating systems, omitting the search path will cause find to search through the current working directory, as we’ve just seen. Unfortunately, that trick doesn’t work for Unix/Unix-like operating systems, such as FreeBSD, macOS, or OpenIndiana. For those operating systems, you’ll always need to specify a search path. To make find search through the current working directory, just use a dot to specify the search path. So, on my FreeBSD virtual machine, the command looks like this:

donnie@freebsd-1:~ $ find . -iname '*.conf'
./anotherdir/yetanother.conf
./anotherthing.CONF
./something.conf
donnie@freebsd-1:~ $

Okay, I know. You’re wondering why I’m mentioning FreeBSD, macOS, and OpenIndiana in what’s supposed to be a Linux book. Well, it’s because sometimes, we’ll want to create shell scripts that work on multiple operating systems, rather than just on Linux. If you include the dot in this command, it will still work on your Linux machines, and will also work on your Unix/Unix-like machines.

You can also specify search paths that aren’t your current working directory. For example, you can remain within your own home directory and search through the entire filesystem like this:

[donnie@fedora ~]$ find / -iname '*.conf'

Of course, this will take much longer than it does to just search through one directory. Also, you’ll encounter errors because your normal user account won’t have permission to go into every directory. To search through all directories on the filesystem, just preface the command with sudo, like this:

[donnie@fedora ~]$ sudo find / -iname '*.conf'

You can perform searches with more than one search criterion. If you separate the criteria with a space, it will be the same as placing an and operator between them. Here, we’ll use the -mtime -7 switch to find all of the .conf files that were modified within the last seven days, and the -ls switch at the end to show detailed information about the files:

[donnie@fedora ~]$ sudo find / -iname '*.conf' -mtime -7 -ls
       18      4 -rw-r--r--   1 root     root          328 Jul 24 17:50 /boot/loader/entries/81085aed13d34626859063e7ebf29da5-6.4.4-100.fc37.x86_64.conf
  3321176   4 -rw-r--r--   1 donnie   donnie        467 Jul 24 16:14 /home/donnie/.config/pcmanfm/LXDE/pcmanfm.conf
      370      4 -rw-r--r--   1 donnie   donnie       3272 Jul 19 16:21 /home/donnie/.config/Trolltech.conf
. . .
. . .
4120762      8 -rw-r--r--   2 root            root                7017 Jul 21 14:43 /var/lib/flatpak/app/com.notepadqq.Notepadqq/x86_64/stable/a049a1963430515aa15d950212fc1f0db7efb703a94ddd1f1d316b38ad12ec72/files/lib/node_modules/npm/node_modules/request/node_modules/http-signature/node_modules/jsprim/node_modules/verror/jsl.node.conf
[donnie@fedora ~]$

To search for .conf files that were modified more than seven days ago, replace the -7 with +7, like this:

[donnie@fedora ~]$ sudo find / -iname '*.conf' -mtime +7 -ls

It’s also possible to create more advanced searches by creating compound expressions. It works like Algebra, in that expressions are evaluated from left to right unless you group some of the terms with parentheses. But, with that, there are a couple of minor catches.

Since the parenthesis symbols have a special meaning in bash and zsh, you’ll want to precede them with a backslash so that bash and zsh won’t interpret them the wrong way. You’ll also need to leave a space between the parenthesis symbols and the terms that they’re enclosing.

Let’s say that we now want to look for all of the .conf files in the /etc/ directory that were either modified within the last seven days, or that were accessed more than 30 days ago. We’ll use the -atime switch to set the access time criterion. The or operator is represented by -o.

[donnie@fedora ~]$ sudo find /etc -iname '*.conf' \( -mtime -7 -o -atime +30 \)
[sudo] password for donnie:
/etc/UPower/UPower.conf
/etc/X11/xinit/xinput.d/ibus.conf
/etc/X11/xinit/xinput.d/xcompose.conf
/etc/X11/xinit/xinput.d/xim.conf
. . .
. . .
/etc/appstream.conf
/etc/whois.conf
/etc/nfsmount.conf
[donnie@fedora ~]$

There are several subdirectories in /etc/ that require root privileges to enter, so I used sudo again, as I did before. Adding the -ls option at the end of the command would show the timestamps on the files, which would tell me which of the two search criteria applies to each specific file.

If you want to find files that belong to only a certain user, you can do that with the -user switch. Add a second criterion to find only files of a certain type that belong to a certain user. Here, I’m searching through the whole filesystem for all .png graphics files that belong to me:

[donnie@fedora ~]$ sudo find / -user donnie -iname '*.png'
/home/donnie/.cache/mozilla/firefox/xgwvyw2p.default-release/thumbnails/9aa3453b0b6246665eb573e58a40fe7c.png
/home/donnie/.cache/mozilla/firefox/xgwvyw2p.default-release/thumbnails/96c0e5aa4c2e735c2ead0701d2348dd6.png
. . .
. . .
/home/donnie/rad-bfgminer/vastairent.png
find: '/run/user/1000/doc': Permission denied
find: '/run/user/1000/gvfs': Permission denied
/tmp/.org.chromium.Chromium.IpK3VA/pcloud1_16.png
find: '/tmp/.mount_pcloudWz4ji1': Permission denied
[donnie@fedora ~]$

Even with full sudo privileges, there are still a couple of directories where I’m not allowed to access. But, that’s okay.

You can use the -group switch to find files that belong to a certain group. Here, I’m looking through my own home directory for either files or directories that are associated with the nobody group.

[donnie@fedora ~]$ sudo find -group nobody -ls
  3344421      0 drwxr-xr-x   1 nobody   nobody          0 Jul 25 18:36 ./share
  3344505      0 -rw-r--r--      1 donnie   nobody          0 Jul 25 18:38 ./somefile.txt
[donnie@fedora ~]$

Note that I’m still using sudo here, because even in my own home directory there are some directories that find won’t access without it. (These are the directories that contain information about Docker containers.)

Conversely, you can use the -nogroup switch to find files that don’t belong to any group that’s listed in the /etc/group file.

[donnie@fedora ~]$ sudo find -nogroup
./.local/share/containers/storage/overlay/994393dc58e7931862558d06e46aa2bb17487044f670f310dffe1d24e4d1eec7/diff/etc/shadow
./.local/share/containers/storage/overlay/ded7a220bb058e28ee3254fbba04ca90b679070424424761a53a043b93b612bf/diff/etc/shadow
./.local/share/containers/storage/overlay/8e012198eea15b2554b07014081c85fec4967a1b9cc4b65bd9a4bce3ae1c0c88/diff/etc/shadow
./.local/share/containers/storage/overlay/7cd52847ad775a5ddc4b58326cf884beee34544296402c6292ed76474c686d39/diff/etc/shadow
[donnie@fedora ~]$

In the Linux/Unix world, everything on the system is represented by a file. Normal users of a system will usually just encounter regular files and directories, but there are many other types of files that will be of interest to a system administrator. The various file types include:

  • Regular files: These are the types of files that a normal user would routinely access. Graphics files, video files, database files, spreadsheet files, text files, and executable files are all examples of regular files.
  • Directories: It seems strange that a directory is a type of file, but that’s just how it is in the Linux and Unix worlds.
  • Character devices: A character device either accepts or supplies a serial stream of data. A sound card or a terminal would be represented by a character device file. You’ll find these files in the /dev/ directory.
  • Block devices: A block device file represents devices that can be accessed in a random manner. Examples include hard drives, solid-state drives, and drive partitions. You’ll also find these files in the /dev/ directory.
  • Named pipes: These devices take the output from one system process and supply it as the input to another system process, thus enabling inter-process communication.
  • Sockets: These are the same as named pipes, except that they can send and receive file descriptors as part of the communications stream. Also, unlike named pipes, sockets can allow two-way data exchange between two processes.
  • Symbolic links: This type of file simply points to either a regular file or directory. This allows users to either access files and directories from multiple places in the filesystem, or to access them by different names.

You can tell what type a file is by doing an ls -l command. The first character in the output for each file is known as the file mode string. This file mode string designates the file type. For example, let’s look at what’s in my home directory:

[donnie@fedora ~]$ ls -l
total 137972
-rw-r--r--.      1 donnie donnie       12111206 Feb 18 13:41 dnf_list.txt
drwxr-xr-x. 15 donnie donnie               4096 Jul 27 16:39 orphaned_files
drwxr-xr-x.   2 donnie donnie                     6 Jul 29 16:53 perm_demo
-rw-r--r--.      1 donnie donnie                 643 Mar 26 15:53 sample.json
[donnie@fedora ~]$

Lines that begin with a - represent a regular file, and lines that begin with a d represent a directory. The various file types are represented as follows:

File mode string

File type

-

Regular file

d

Directory

c

Character device

b

Block device

p

Named pipe

s

Socket

l

Symbolic link

Table 2.1: File type designators

There may be times when you’ll need to locate all files of a certain type. You can do that with the -type option, like so:

[donnie@fedora ~]$ sudo find / -type p -ls
      545    0 prw-------   1 root     root            0 Jul 31 15:20 /run/initctl
      542    0 prw-------   1 root     root            0 Jul 31 15:20 /run/dmeventd-client
      541    0 prw-------   1 root     root            0 Jul 31 15:20 /run/dmeventd-server
        6      0 p---------    1 donnie   donnie     0 Jul 31 15:29 /run/user/1000/systemd/inaccessible/fifo
     1228   0 prw-------   1 root     root            0 Jul 31 15:21 /run/systemd/inhibit/2.ref
     1193   0 prw-------   1 root     root            0 Jul 31 15:21 /run/systemd/inhibit/1.ref
     1324   0 prw-------   1 root     root            0 Jul 31 15:29 /run/systemd/sessions/3.ref
     1311   0 prw-------   1 root     root            0 Jul 31 15:29 /run/systemd/sessions/1.ref
        8      0 p---------    1 root     root            0 Jul 31 15:20 /run/systemd/inaccessible/fifo
      112    0 prw-------   1 root     root            0 Jul 31 15:20 /var/lib/nfs/rpc_pipefs/gssd/clntXX/gssd
  [donnie@fedora ~]$

As you see, I’m using the -type p option to search for all named pipe files.

Now, let’s consider the previous example in which we searched for all files that end with a .conf filename extension:

[donnie@fedora ~]$ sudo find / -iname '*.conf'

This command only found regular files because they’re the only types of files on the system that have the .conf filename extension. But, let’s now say that we want to search through the /etc/ directory to find all subdirectories with the conf text string in their names. If we don’t specify a file type, we’ll see regular files, symbolic links, and directories:

[donnie@fedora ~]$ sudo find /etc -name '*conf*' -ls
   329486       4 -rw-r--r--   1 root     root          351 Jul 27 07:02 /etc/dnf/plugins/copr.conf
   329487       4 -rw-r--r--   1 root     root           30 Jul 27 07:02 /etc/dnf/plugins/debuginfo-install.conf
  8480155      4 -rw-r--r--   1 root     root           93 May 18 04:27 /etc/dnf/protected.d/dnf.conf
. . .
25325169      0 lrwxrwxrwx   1 root     root           43 Jul 29 18:19 /etc/crypto-policies/back-ends/bind.config -> /usr/share/crypto-policies/DEFAULT/bind.txt
 25325172      0 lrwxrwxrwx   1 root     root           45 Jul 29 18:19 /etc/crypto-policies/back-ends/gnutls.config -> /usr/share/crypto-policies/DEFAULT/gnutls.txt
. . .
5430579        0 drwxr-xr-x   2 root     root           25 Sep 19  2022 /etc/reader.conf.d
  8878157      0 drwxr-xr-x   3 root     root           27 Dec  8  2022 /etc/pkgconfig
  8927250      0 drwxr-xr-x   2 root     root           83 Nov 16  2022 /etc/krb5.conf.d
. . .
[donnie@fedora ~]$

We’ll use the -type d option to narrow things down:

[donnie@fedora ~]$ sudo find /etc -name '*conf*' -type d -ls
 17060336      0 drwxr-xr-x    2 root     root           41 Dec  8  2022 /etc/fonts/conf.d
 25430579      0 drwxr-xr-x    2 root     root           25 Sep 19  2022 /etc/reader.conf.d
  8878157       0 drwxr-xr-x    3 root     root           27 Dec  8  2022 /etc/pkgconfig
  8927250       0 drwxr-xr-x    2 root     root           83 Nov 16  2022 /etc/krb5.conf.d
 25313333      0 drwxr-xr-x    2 root     root            6 Feb  1 17:58 /etc/security/pwquality.conf.d
 25395980      0 drwxr-xr-x    2 root     root           30 Dec  8  2022 /etc/X11/xorg.conf.d
 17060487      0 drwxr-xr-x    2 root     root            6 Aug  9  2022 /etc/pm/config.d
. . .
. . .
 16917753      0 drwxr-xr-x    2 root     root           33 Jul 29 18:11 /etc/containers/registries.conf.d
[donnie@fedora ~]$

Cool. We now only see the directories, which is exactly what we want.

As I said before, there are a lot of options that you can use with the find utility. (Enter man find to see them all.)

Now, with the introduction to find out of the way, let’s look at how to use find to perform multiple actions with one command.

Performing Multiple Actions with find

Our next trick contains a bit of a twist. We’ll use find's -exec and -ok option switches to make find perform some sort of action on each file that it finds. First, find finds the files. Then, it causes another command to run that will take some sort of action on the files. Here’s how it works.

The -exec and -ok switches tell the shell to perform a second command only if the first command produces valid output. It then uses the output of the first command (find) as arguments for the second. The difference between the two switches is that -exec causes the desired action to automatically execute on each file without prompting the user. The -ok switch will cause the action to stop after each file that find finds, asking the user whether or not to proceed with the action for that file. Here, we’re searching the entire filesystem for all .zip files that are more than 30 days old, and copying them to the /home/donnie/ directory. (Note that I’m still using sudo so that I can access all directories.)

[donnie@fedora ~]$ sudo find / \( -mtime +30 -iname '*.zip' \) -exec cp {} /home/donnie \;

The {} after the cp command tells bash or zsh, “Take the results from the find command, and put them here as the arguments”. Note that this command sequence has to end with a semi-colon. But, since the semi-colon has special meaning for bash and zsh, you must precede it with a backslash so that bash and zsh will interpret it correctly.

Also, note that you must have a blank space after the first parenthesis, and another blank space before the backslash that precedes the last parenthesis.

Now, suppose that you only want to copy over some of the files that you find. Just replace the -exec switch with the -ok switch. It works the same as -exec, but it will ask permission before performing an operation on a file. You’ll have to enter either y or n before continuing to the next file.

The same principle also works for removing files.

[donnie@fedora ~]$ sudo find / \( -mtime +30 -iname '*.zip' \) -ok rm {} \;

Let’s now suppose that Vicky, Cleopatra, Frank, and Goldie are all creating graphics for some sort of project. They’re supposed to place the graphics files into the graphics subdirectory that each of them have in their own home directory. Sometimes they forget though, and place the files into their top-level home directories, as we see in the following diagram:

Figure 2.1: Some of these graphics files are in the wrong place.

Now, let’s get a bit of hands-on practice with this.

Hands-on Lab – Using find to Perform Other Commands

For this lab, use a Fedora, Debian, or Ubuntu virtual machine. (I’ll provide instructions for all of them.)

Let’s say that we want to copy everyone’s graphics files into a common backup directory.

  1. First, create the /backup directory, like this:
    [donnie@fedora ~]$ sudo mkdir /backup
    

For our present purposes, just leave ownership and permissions settings as they are.

  1. Next, create user accounts for Vicky, Cleopatra, Frank, and Goldie, and assign a password to each account. On Fedora, the commands would look like this:
    donnie@fedora:~$ sudo useradd frank
    donnie@fedora:~$ sudo passwd frank
    

On either Debian or Ubuntu, use the interactive adduser command, which both creates the user account and sets the password. It looks like this:

donnie@debian12:~$ sudo adduser goldie
  1. Log into each user’s account, create a graphics directory in each user’s home directory, and then create some fake graphics files. Here are the commands to do that:
    goldie@fedora:~$ touch goldie02.png
    goldie@fedora:~$ mkdir graphics
    goldie@fedora:~$ cd graphics/
    goldie@fedora:~/graphics$ touch {goldie01.png,goldie03.png,goldie04.png}
    goldie@fedora:~/graphics$
    

The touch command is actually meant to be used by programmers for purposes that I won’t go into here. But, it’s also handy for situations like this, when you just need to create some fake files for testing purposes. By enclosing a comma-separated list of filenames within a pair of curly braces, you can create multiple files with just one single command. To verify that, let’s peek into the graphics directory:

goldie@fedora:~/graphics$ ls -l
total 0
-rw-r--r--. 1 goldie goldie 0 Mar 23 13:27 goldie01.png
-rw-r--r--. 1 goldie goldie 0 Mar 23 13:27 goldie03.png
-rw-r--r--. 1 goldie goldie 0 Mar 23 13:27 goldie04.png
goldie@fedora:~/graphics$
  1. For this step, you’ll need to log back into your own user account. You want to be sure to get all of the graphics files, even if they’re in the users’ top-level home directories, and copy them into the /backup/ directory. Your command and results would look like this:
    [donnie@fedora ~]$ sudo find /home -name '*.png' -exec cp {} /backup \;
    [donnie@fedora ~]$ ls -l /backup/
    total 0
    -rw-r--r--. 1 root root 0 Jul 28 15:40 cleopatra01.png
    -rw-r--r--. 1 root root 0 Jul 28 15:40 cleopatra02.png
    -rw-r--r--. 1 root root 0 Jul 28 15:40 cleopatra03.png
    -rw-r--r--. 1 root root 0 Jul 28 15:40 cleopatra04.png
    -rw-r--r--. 1 root root 0 Jul 28 15:40 frank01.png
    -rw-r--r--. 1 root root 0 Jul 28 15:40 frank02.png
    -rw-r--r--. 1 root root 0 Jul 28 15:40 frank03.png
    -rw-r--r--. 1 root root 0 Jul 28 15:40 frank04.png
    -rw-r--r--. 1 root root 0 Jul 28 15:40 goldie01.png
    -rw-r--r--. 1 root root 0 Jul 28 15:40 goldie02.png
    -rw-r--r--. 1 root root 0 Jul 28 15:40 goldie03.png
    -rw-r--r--. 1 root root 0 Jul 28 15:40 goldie04.png
    -rw-r--r--. 1 root root 0 Jul 28 15:40 vicky01.png
    -rw-r--r--. 1 root root 0 Jul 28 15:40 vicky02.png
    -rw-r--r--. 1 root root 0 Jul 28 15:40 vicky03.png
    -rw-r--r--. 1 root root 0 Jul 28 15:40 vicky04.png
    [donnie@fedora ~]$
    

What I’ve shown you here just barely scratches the surface of what you can do with find. To see the complete list of search criteria that you can specify, open the find man page and scroll down to the TESTS section.

We’ll look at some more find examples a bit later. For now though, let’s look at how to create recursive commands.

You have been reading a chapter from
The Ultimate Linux Shell Scripting Guide
Published in: Oct 2024
Publisher: Packt
ISBN-13: 9781835463574
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image