Questions from Last Time
How to see all active aliases in a bash session?
lol just use the alias
command (no args).
How to unbind an alias?
The unalias <aliasname>
command.
Text Filtering / Finding
Ever been looking for some file that has a specific bit of text inside but just can't remember which it is?
Do you have long outputs from certain bash commands that you just don't have the time to search?
If you answered yes to either of these questions, and a variety that I have yet to ask, then is text filtering for you!
The grep "<pattern>" <files>
("general regular expression parser") command finds text contained within the given files, and
returns the lines containing them.
Use the -r
parameter to search directories and subdirectories as well.
A couple of things to note about the pattern:
It is generally safer to surround it in quotations in the event that you wish to use special characters in the pattern
It is, of course, a dreaded regex (regular expression) that is akin to the formal regular expression syntax
grep
can be used to simply search for words and phrases like grep "waldo" WheresWaldo.txt
However, when we want to exploit the more powerful regex capacities of grep, we need some more knowledge of the syntax.
Some of the grep regex special characters include:
.
match a single character[characters]
match any single character listed in the brackets^pattern
matches lines that begin with the patternpattern$
matches lines that end with the patternpattern*
matches zero or more instances of the patternpattern{n}
matches some n occurrences of the given pattern
Try a couple of grep examples now; consider at least thinking about: history | grep
and grep | grep
.
And now, for a challenge!
If you are in class at the time this is presented, and can turn something in, you'll earn some sort of bonus!
Your task: somewhere on this very webpage (lecture-3T.html), I have hidden a secret message sharing a line with the word "SECRET". You must find this line and then store the result in a text file called "Secret.txt" in a *single* bash line (possibly multiple commands, piping, and redirecting involved).
Turn in a paper with this single command and the names of any group members with whom you worked.
You'll get a *super* bonus if, in addition to the above, you make the single line as hilariously elaborate as possible while accomplishing the same task.
Text Replacement
Just as grep
supports the "find" operation that we're all so familiar with, so too should we have a more sophisticated "replace" feature!
The sed "<rule>" <files>
("stream editor") can be used to perform highly customizable find-and-replace across multiple files.
Some things to note:
The sed rule follows the (most basic) syntax: "s/<pattern>/<replacement>/g"
The
pattern
is a regular expression just like those used to find text ingrep
The
replacement
is what to substitute for any matched patternThe leading
s
is to substitute any found patterns with their replacements; this is useful for avoiding loops in replacement like:"s/loop/loop loop/g"
The trailing
g
is for global replacement; without it, only the first occurrence of the matched pattern will be replaced on each line.
The -i
parameter can be used to modify documents in-place; without it, the replacements are simply made in the output of sed to stdout.
Provide the sed
command to replace all instances of "vi sucks" to "emacs sucks" in all files in your Documents directory.
sed -i "s/vi sucks/emacs sucks/g" ~/Documents/*
Provide the sed
command to replace all tabs with 4 spaces in all files in your Documents directory.
sed -i "s/\t/ /g" ~/Documents/*
Provide the sed
command to remove all *trailing* spaces in all files in your Documents directory.
sed -i "s/[ ]$//g" ~/Documents/*
sed
Matching
Thus far, we've seen sed
used to replace text where the replacement was independent of the matched text to replace.
However, it stands to reason that we'd be interested in making replacements that are in some way related to the matched text.
The &
character can be used in the replacement argument as a placeholder for the text matched in the pattern.
The sed
command for replacing all integer literals with decimal counterparts might look like:
sed "s/[0-9][0-9]*/&.0/g"
sed
with Pipes
Using sed
without the -i
parameter serves a popular purpose in use with piping output from other commands.
For example, re-running commands from one's bash history with different arguments than those previously given might look like:
history | grep curl | grep "forns.lmu.build" | sed "s/lecture-[0-9][TR]/lecture-3T/g" | bash
Compound Commands
Both grep
and (to a lesser degree) sed
operate on a line-by-line basis, and aliases
are generally used to provide a CLI
shortcut to a "one line" command.
However, often, we want to provide multiple commands in a single "operation," some of which depend on the success or failure of previous commands in a sequence.
For this, we might turn to Compound Commands.
A compound command involves one of a number of operators used to chain individual commands together into a single operation.
Pipes and redirection are somewhat related to compound commands, except that the latter do not necessarily depend on the output of another command so much as they simply represent a sequence of different commands to be executed.
Let's take a look at a few now:
Separator ;
Whenever we want to chain commands together into a single "one-liner" mega-command, we can use a simple separator between them.
The separator (;)
conjunctive will execute commands in a sequence and is used to indicate where the previous command ends and the next
begins. Its syntax is:cmd1 ; cmd2 ; ...
where cmd2 will execute after cmd1.
Suppose we want a single alias to print the inodes of files in a variety of locations, we might do something like:
alias lotsofinodes='ls -i ~/Documents ; ls -i /mnt/usb'
AND &&
In other cases, we are interested in only continuing down a chain of commands (probably some military joke to make here but meh) if earlier parts of the chain are successful.
In these scenarios, it's important that we do not continue executing commands when important predecessors have not completed.
The AND (&&)
conjunctive with syntax cmd1 && cmd2
will only execute cmd2 if cmd1 completed successfully
(i.e., exited with a success exit code, typically 0
).
Suppose we wanted to only perform some command if a directory exists; we could:
cd ~/Documents/secrets && touch secrets.txt
OR ||
Moreover, we can implement logical-OR to only continue down a sequence of commands if the predecessors do not terminate successfully.
The OR (||)
conjunctive with syntax cmd1 || cmd2
will only execute cmd2 if cmd1 failed (i.e., exited with an error status code,
typically anything but 0
).
Suppose we wanted to only make a directory if it did not exist; we could:
cd ~/Documents/secrets || mkdir ~/Documents/secrets
Grouping ()
Of course, in the same way that we might group boolean logic operations in programming languages, so too can we do so in bash.
The grouping ()
operator treats a compound command as a single command in the context of yet another compound command.
This will look something like:
(cmd1 || cmd2) && (cmd3 || cmd4)
Homework 1
At this point in the lecture, we'll take a quick look at HW1 before letting out for today!