Linux "Tips" and the websites that publish them.

An article caught by an "aggregator" site caught my eye the other day: The "EFY Times" published an article of reader-submitted "tips" entitled "20 Linux Tips Worth Trying". Now, being someone who is interested in all the "tips" I can get, I read the article. And regretted it.

It appears that the "EFY Times" was lazy. They didn't vet the "tips" at all. Instead of a list of dependable, well-understood tips, the article contained a lot of poorly selected, side-effect-ridden, and just plain sloppy suggestions from (apparently) self-taught linux users.

I blame the "journalists" who run EFY Times. They should have tested the "tips", and either filtered out the wonky ones, or fixed them.

I'm not going to try to "fix" all the tips, but here's three that I took exception to.

Tip # 2 - Search and delete files from a folder

The original author wrote

If you want to delete all the .lock files from a folder, use the following command:

find -name *.lock | xargs rm -rf

This will find all the files with the .lock extension and delete them. This can be done for any files that you need to delete.

First off, there are no "folders" in Linux. What the author meant was "directories".

Next, we need to explore what the find(1) command does. The command takes a list of directories to search, and searches them for files that fit specific criteria. If not given a search directory, it starts in the current working directory. find searches the directory, and it's subdirectories for files that match the criteria. Unless otherwise directed, find will print the names of any files that match the search criteria.

One of finds search criteria is to search by name. Wildcards are permitted in the given name; find will expand and match as required.

So, the author suggests that

find -name *.lock

will "find all the files with the .lock extension", and implies that this will happen only in the (current) "folder".

Not so.

First off, since the value that followed the -name option wasn't singlequoted, the shell will expand *.lock into a list of files (in the current directory) that all have the .lock suffix. The first name listed will satisfy the -name option, and the rest of the filenames will be treated as additional options to find. This will cause find to terminate with an "invalid option" error.

So, to be true to the intent, the author's command should look more like

find -name '*.lock'

Next, find will search for anything that matches the given name model, including directories. And, because find will search the subdirectories of the directory it was directed to search, it looks for matching names not only in the starting directory, but in the full directory tree that starts at the starting directory. Thus, the author's find command will over-achieve, and match

  • files in the tree that match the name regex *.lock,
  • directories in the starting directory that match the name regex *.lock, and
  • directories in the tree that match the name regex *.lock

along with the expected files in the starting directory that match the name regex *.lock.

Finally, the author's pipeline will pass this list of file and directory names to
xargs rm -rf. Now, xargs simply groups the names together, and then passes them along as a list to the command listed in it's arguments. That makes the real target command

rm -rf

.

This rm(1) (remove) command explicitly forces the removal of the listed file or directory, and does so recursively. Thus, any *.lock files will be removed, and any *.lock subdirectories will also be removed, including their contents.

So, we have the author's original "tip", which would have caused find to terminate with an error if there were more than one matching file in the current directory, and to recursively delete any directories that matched the filename criteria that the author had required. Dangerous "tip" that - one that I'll avoid.

How would I delete any lock files in the current directory? To be safe, I'd want to ensure that they were files, that they were in the current directory, and that they matched the filename regex that I wanted. So

find -maxdepth 1 -type f -name '*.lock' | xargs rm -f


Tip #14 - Sudoing with Fedora

The original author wrote

Ever felt tired of entering the super-user password after typing ‘su –c’ again and again? Type ‘su -c visudo’ just once and uncomment the following line:

# %wheel ALL=(ALL) ALL

Replace ‘wheel’ with your sudo username. So if the username is egghead, the line becomes…

%egghead ALL=(ALL) ALL

Save and quit. You’re good to use egghead as the sudo user.

To make it easier to have multiple system administrators (all who need root access) to manage the system without conflicting with each other or compromising the root logon, early system admins worked out this neat facility called sudo(8). The /etc/sudoers file (see sudoers(5)) lists usernames and the things that they can do as root. It also lists group names, and the things that users of that group can do. They called one special group wheel, and allowed members of that group to do anything that root can do.

In sudoers "User" rules, the username is the first field, followed by a specific command, or a general rule like "ALL". So, if user egghead needs a rule, then that rule would look like

egghead ALL=(ALL) ALL

For Linux groups, a similar pattern is used, except that the groupname is prefixed with a percent sign. So for a rule for all members of the wheel group, the rule would look like

%wheel ALL=(ALL) ALL

Now, the author tells us to make a rule that looks like

%egghead ALL=(ALL) ALL

in order to permit user egghead to do anything that root can do. Well, this rule doesn't do that. It explicitly permits any user within the egghead group root-like powers. Hmmmm....... not what we wanted.

And, the author tells us to build this rule by modifying the %wheel rule that gives all the system administrators root-like powers.

So, we take root away from the system admins, and give it to another group.

No.

Instead, how about this: Either

  1. make user egghead part of the wheel group (with usermod(8) or some similar user management tool), and let him inherit his root-like powers from the %wheel rule, or
  2. in /etc/sudoers, write a user rule for egghead and not a group rule.

That's better.

Tip #15 - Let your Linux system welcome you

Issue the following script and name it welcome.sh

echo “Hi zades you are welcome today is “ | festival --tts
date| cut -d” “ -f 1-3 | festival --tts

Now put the command sh welcome.sh at start-up. This will allow the script to run every time you log in to your system. Once done, restart your system to hear the message that is written in the Echo command.

The festival command is used to change the text to voice. You can use this command in many ways according to your creativity. Do remember to check that you have festival installed before trying this tip.

This one is just too complex. echo, date, cut and pipelines, all to get some simple text into festival. And don't get me started on the completely unclear instructions on where to put this wonderful pipeline. I really don't know what he is talking about.

Let's try to simplify that pipeline somewhat.

The author wants festival to say hello, and give him the date. OK, let's look at the date part first.
His command is

date| cut -d” “ -f 1-3 | festival --tts

Without any options, the date(1) command prints out

  1. the short form of the name of the current day of the week, followed by
  2. the short form of the name of the current month, followed by
  3. the numeric form of the current day of the month, followed by
  4. the numeric form of the current time (in hh:mm:ss format), followed by
  5. the abbreviation for the current timezone, followed by
  6. the numeric form of the current year

This list of items is passed to cut(1), which (using a space as a delimiter) selects out the first three fields (the day of the week, the month name, and the day of the month).

This list of three items is then passed to festival to speak.

But date can accept commandline options, and one of those options allows the issuer to select exactly what information date will supply. If, for instance, I want to see the short name for the current day of the week followed by the short name of the current month, followed by the numeric form of the current day of the month, I only have to pass date an argument that selects those fields. Like

date '+%a %b %d'

.

And, if I did that, my pipeline to festival would look like

date '+%a %b %d' | festival --tts

.

Oh, but wait. date can take more in that format argument than just date formatting information; it can take literals as well. So, if, for instance, I wanted date to present me with a greeting, and the date, I could. In fact, it would look something like

date '+Hi Lew you are welcome today is %a %b %d'

and to have festival speak it, I might code the pipeline as

date '+Hi Lew you are welcome today is %a %b %d' | festival --tts

Hey, look at that, I've recreated the author's command, but without the extra coding.

But, why stop there? I might even prefer to have festival speak the proper full name of the day of the week and the month, and make that pipeline

date '+Hi Lew you are welcome today is %A %B %d' | festival --tts

Comments

Let's give a concrete example of the flaws of the original, and my suggested alternative

We start in a directory that has two .lock files, and two subdirectories. One of the subdirectories has an additional .lock file, while the other subdirectory contains a further subdirectory named mno.lock. Subdirectory mno.lock holds a single file that is not a .lock file.


$ ls -lR
.:
total 8
-rw-r--r-- 1 lpitcher users 0 2013-08-15 15:06 abc.lock
drwxr-xr-x 2 lpitcher users 4096 2013-08-15 15:06 def
-rw-r--r-- 1 lpitcher users 0 2013-08-15 15:07 def.lock
drwxr-xr-x 3 lpitcher users 4096 2013-08-15 15:07 jkl


./def:
total 0
-rw-r--r-- 1 lpitcher users 0 2013-08-15 15:06 ghi.lock


./jkl:
total 4
drwxr-xr-x 2 lpitcher users 4096 2013-08-15 15:07 mno.lock

./jkl/mno.lock:
total 0
-rw-r--r-- 1 lpitcher users 0 2013-08-15 15:07 pqr

First off, let's just see what the author's original find command would do. I said that it would error out because shell expansion of the unquoted -name regex would result in a list of files, which find would try (and fail) to interpret as commandline arguments. Let's see:


$ find -name *.lock
find: paths must precede expression
Usage: find [-H] [-L] [-P] [path...] [expression]

It looks like I was correct. The shell expanded the unquoted regex into a list of matching files, and find took the (substituted) name of that second .lock file in the top level directory as an option.

Now, let's test the find using quotes to prevent shell regex expansion.


$ find -name '*.lock'
./def/ghi.lock
./jkl/mno.lock
./def.lock
./abc.lock

OK, so we see that, with shell regex expansion suppressed, the original author's find returned not only the two .lock files in the top directory, but the the .lock file in one subdirectory, and the .lock directory in the other subdirectory. If we had recursively removed these files, we would have lost more lock files than we had expected to, and lost a full directory of other files as well.

Now, let's see what my alternative find command would have returned:


$ find -maxdepth 1 -type f -name '*.lock'
./def.lock
./abc.lock

With this version, we only get the two .lock files in the top directory, and nothing more.