| 2010 Grut Training Schedule | 2010 Grut Course Catalog |
Sign up to have weekly UNIX hints, tips, and tricks sent directly to your e-mail (or, scroll down to see the published tips and tricks.)
Scripting Hints, Tips, and Tricks
|
My "find" commands run too long. How can I speed them up by skipping directories? |
|
Solution:
Use the
–prune option with find. Just
make sure
you put your –prune before your positive criteria.
if
the name is "sh*" (begins with
sh), print its name.
-name proc
–prune –o \
-name tmp
–prune –o \
-name "1*"
–print
2> /dev/null |
|
Notes: The \ as the last character on a command line is the shell's line continuation character. Also, remember if you throw away your errors (2> /dev/null), you'll be disregarding any command syntax errors as well. Just make sure you have the command format correct before you add the 2> /dev/null. |
|
How do I detect files that contain carriage-returns? (usually as the result of a binary ftp from Windows to UNIX). |
|
Solution: A
carriage-return is an octal 015. We
can
use the "od –b" command with
grep to determine if the file contains carriage-returns.
Explanation: In
the above, the script looks at all of
your command line
arguments (should be ASCII files to interrogate for carriage returns.) If
the file is not readable or the file is
not a regular file, skip it.
Type out
the contents of the file, pipe the output into od –b, strip
off
the portion of
the od –b output that is a byte offset (with the cut command)
,
and then check
for a 015. If the 015 was found by grep, grep's return code will be 0 (success.) If so, print that the file contains carriage returns. |
|
Note: If you pass binary files, it is likely that you will find 015 patterns, even though they will not be treated as carriage-returns. |
|
I have several ways of accomplishing my UNIX task. Which is the most efficient? |
|
Solution: Write a
little, reusable test harness to iterate over your algorithms multiple
(or many) times.
I have a test harness
named
mytester.
First, the invocation
syntax:
|
|
In the above example, all three methods produced extremely similar internal time measurements (sys and user.) However, the Perl version was significantly slower in real time, most likely due to the size of actually loading the Perl executable (which is much larger than awk or tr) many, many times. |
|
I want to quickly get some information about a particular command, regardless of where on the file system it is stored. |
|
Solution: Use the
which
command (in reverse tiks with the command
you're interested in.)
|
|
Notes: This tip, obviously, only works with commands that are physical commands (stored on the file system), not built-ins, keywords, or functions. Also, the command would have to be in a directory that is noted in your PATH variable (most should be.) |
|
My shell terminates when I type <Ctrl>D. I don't like that. How do I make it stop? |
|
Solution: set –o # set
dash lowercase Oh Look for the option named "ignoreeof". If it is turned OFF, the shell will not ignore EOF (in other words, the shell will respond to a <Ctrl>D as an exit.) To
turn "ignoreeof" ON, so the shell will
ignore the <Ctrl>D, your command is: set –o ignoreeof If you put the above command in your .profile (or .bash_profile) it will affect only your login shell. All other subsequent shells should respond to <Ctrl>D as an "exit". (That's the way I like to set my environment up.) |
|
Notes: You
can, of course turn ON any of your listed options in the same way we
did above.
To turn a shell option
OFF: set +o option_name. Also,
some of the options have shortcuts for
turning them on and off.
For example: set –x is
the same as: set –o xtrace The shell options are NOT inherited by subsequent shells. For that type of behavior, you would need to put your option settings in .kshrc or .bashrc. |
|
My terminal is hung up. Nothing I type is visible. No output is visible. What's up with that? |
|
Possibility 1: Solution 1:
Solution 2: Possibility 3: Solution 3: |
|
Note: The "ps" command in solution 3 might be a good candidate for an alias named "psu". |
| I want to scriptomatically find files of a particular type. I want the file type I'm searching for to be part of the name of the script. |
|
First Off: Step 1:
Create
a script
file named findperl containing the following. #!/usr/bin/ksh Step 2:
Save
the file. Step 3:
chmod
740 findperl
# Give
the script executed privileges Step 4:
./findperl
# Run
it.
The ./ is not necessary
if your current Step 5:
Modify
your findperl script to look at the name of the script to yank the
"perl" portion from it.
We'll then use the
portion of the file name for the pattern
Step 6:
Rerun
it.
You should get results
identical to
the
first version. Step 7: Create the ASCII file version: ln
findperl findascii Step 8: Create the C version ln
findperl findc
# Just link to the
file |
|
Note: All
your scripts will need to be named findx, where x is the pattern you're
looking for.
The pattern has a
non-alpha
character list around it with the grep, Keep
in mind that we're using hard-links for
the script file names.
A new name, via a
hard
link, only takes up one slot in a directory file. |
|
I want to get a list of all my Perl Scripts, C Programs, ASCII files, etc. |
|
The Problem: |
|
Note: Make
sure you understand that reverse tiks have a quite different function
than the similar looking single-quote. Reverse
tiks always have a command or command sequence inside of them that,
should, return some output.
Reverse tiks
pass that output to something else. |
|
I want to remove some files with names beginning with a hyphen. |
|
The Problem: For
example: I type the following
command sequence: rm
–x and
receive this error: rm:
rm
invalid option -x Solution
(fully qualify the
file name) rm
/home/barney/-x # or rm ./-x
# The
file is right here, in my current directory. (dot-slash prefixed) |
|
Explanation:
A
fully qualified file name tells the shell exactly where the file lives. You
fully qualify a file by prepending the file's directory in front of the
file name.style="color: black;">
The shell assumes that
unqualified file names live in your current directory. Additional note: You can certainly always qualify your file names for any command whether you need to or not (as a matter of fact, it's considered good practice in shell scripts.) However, the non-full-qualification of file name reference, allowed by the shell, sure saves a lot of typing. |
|
I want to save a man page. I don't want to see those nasty backspaces. |
|
The Problem: man
ps > psman.out Then, I pull up psman.out in vi, and there are tons of strange character sequences. Solution
(pipe the output
through col –b before writing it) man ps | col
–b
> psman.out |
|
Explanation: The backspace
characters are
used in the man command's output in case the document is actually sent
to an impact printer (dot-matrix, daisy-wheel, line, etc.)
It's
sort of the poor-man's way of bolding (just keep hammering the same
character in the same place a couple of times.)
Additional
note: If
you're not happy with the man command's paging mechanism (more, less,
pg, etc.) you can change it to whatever you like with the command: export
PAGER=more This
essentially disables paging Just make sure you point the PAGER variable to a valid paging command. |
|
I want to store my command history in separate files by month or day. |
|
The Problem: Solution
1 (Store history in
monthly files): |
|
Explanation: Solution 1
|
|
How to
quickly look for
patterns through an entire directory chain. |
|
The Problem: Solution 1: |
|
Explanation: -exec
tells find to execute a command for each file, in this case, the grep
command. -H
is
the grep
option to display the file name, in addition to the record that was
found. “Mr.
Peepers” is the pattern we are looking for in each record of
the files. Don’t
forget, when you use –exec,
the {} is the placeholder that receives the file name of the
“found” file. Make
sure you put a space before the protected semi-colon (\;). Disregard
the error messages (2> /dev/null) This
is a good extension for many commands if you are not interested in
viewing errors. Solution
2 Solution
3 Notes:
Some
grep versions support a recursive option which may be a more concise
command.
Check your man
pages for details on your version. The
above solution should work on all UNIX platforms. |
| ^M at the end of records. |
| The
Problem: I've got a ^M at the end of each of my records. What are they and how can I get rid of them? Solution 1: tr -d "\012" < input.file > output.file mv output.file input.file Solution 2: sed 's/^M//' input.file > output.file mv output.file input.file |
| Explanation: UNIX uses a single character to specify newline. Many other operating systems use 2 characters (carriage return and line-feed.) When a file gets transferred from location to location in a binary mode, no translation is made from the CR / LF to UNIX's preferred single-character newline. Hence, you get stuck with an additional character (the CR) and it looks funny, is irrritating, and can cause problems. CR happens to be octal value 012 which shows up as ^M (Ctrl-M) when displayed on most terminals. All we need to do is remove that character. Solution 1 uses the tr "translate" command which can only read from STDIN, hence the < for inputting. The -d option is for deleting characters, as opposed to tr's normal function of translating characters. If you want to type a Ctrl-M (as is shown in solution 2) on the command line, you will probably need to type Ctrl-V before the Ctrl-M. That just tells the shell that an embedded control sequence follows. (That is not a carat M. It is a Ctrl-M.) Following either of the translating commands, rename your output file back to your original file name. Make sure you don't redirect ( > ) over the filename you want translated in whichever command you use.. |
| How to fix your backspace key. |
| The
Problem: I get weird characters printed to the terminal when I attempt to backspace, like ^H or ^?. Solution: If you see ^H run this command: stty erase ^H If you see ^? run this command: stty erase ^? Or, for either case: stty erase (and then, just type the backspace key which will generate the proper character sequence) |
| Explanation: Your backspace key emits character sequences, just like the "A" key or "Z" key. However, backspace performs terminal manipulation other than just printing a character. Your emulator may be expecting a different character sequence than the one that is currently set up to backspace. The stty command (set your terminal characteristics) is used to attach character sequences to particular keystroke combinations on your tty (terminal.) You may want to put either of the "stty erase" commands above in your .profile (or .bash_profile.) Or, if you prefer, you could create an alias as in: alias fixtty="stty erase ^?" and put that statement in your .profile. Then, whenever you need it, just type in fixtty |
| How to quickly create an empty file not knowing whether a file under that name already exists or not |
| The
Problem: I need to create a new, empty file. A current file with the same name may already be in existence. Solution 1 (works): rm my.new.file 2> /dev/null touch my.new.file Solution 2 (better): cat /dev/null > my.new.file Solution 3 (best): cp /dev/null my.new.file |
| Explanation: Solution 1 will works by attempting to remove the file. If the file does not exist, rm generates an error, which we disregard. We then, unconditionally create the new file. However, if the rm failed because we did not have write privileges on the file, we would not see that error either and think that touch created a new file. In fact, it would have left the existing file intact. Solution 2 types the contents of an empty file and redirects it over the top of my.new.file. Although we often use /dev/null to throw away output and errors, we can also use it as a file containing nothing as input. The problem with this solution is we are depending on the shell to allow us to clobber a file, via redirection, that may be in existence. The shell, through its option settings, may disallow that. Solution 3 merely copies the contents of the non-existent file to a new file. Shell redirection does not get involved. Note: With all three solutions, of course, we would need write privileges on the directory (and the file, if in existence) to create the file. |
| Removing the directory name from a fully qualified file name |
|
The
Problem: FILENAME=`basename $FILENAME` # Those are reverse tiks, not apostrophes FILENAME=${FILENAME##*/} # Those are curley brackets, not parenthesis |
|
Explanation: Solution 1 works perfectly. However, basename is an external command that has to be loaded before being executed. Anytime an external command has to be loaded by UNIX a significant efficiency penalty is assessed. working with a variable (FILENAME is the variable.) The ## construct is a "left" match-and-strip construct. ##, in this construct indicates to the shell to remove the longest pattern that matches on the wildcard pattern of */ (anything (*) through the last slash.) |
| Building Hang-Up immunity into your shell script so it survives you logging off |
| The
Problem: My background jobs don't survive me logging off if I forget to "nohup" them. Solution: At the top of your shell script, after the #!/usr/bin/ksh (or whatever), do this: trap "" 1 # That's trap space double-quote double-quote space 1 At the bottom of your shell script: trap 1 |
|
Explanation: trap
alters the
behavior of a received signal. When you log out of your
shell,
the shell passes signal 1 (the HangUp) signal to all child The double-quote
double-quote contains the altered functionality of signal 1 (in this
case, do nothing.) When your running child process The "trap 1"
at
the bottom of the script will not do anything unless you run your
script as part of your current process (by sourcing the |