Tools 2

Nov 22, 2009 23:09

In the spirit of putting my money where my mouth is, here are some stupid terminal tricks I've done over the past however-long, taken from my zsh history.

I considered writing a general introduction to Unix-like terminals, but that's been done to death.

pngcrush everything in a directory:

mkdir _pngcrush_tmp && pngcrush -d _pngcrush_tmp -rem alla *.png && mv _pngcrush_tmp/*.png . && rmdir _pngcrush_tmp

This is an alias I have, actually. Create a directory, crush everything into it, then move it all back and remove the directory.

pastebin chunks of an irc log:

tail -n 100 irclogs/network/\#channel.log | pastebinit
grep foo irclogs/network/\#channel.log | pastebinit

Undelete everything deleted from a git repository, without affecting anything else:

git status | grep deleted | cut -c 15- | xargs git reset HEAD

git status lists changes to known files; grep filters that down only to deleted files; cut chops off the first 15 characters (which gets rid of the "deleted:" prefix); and the resulting list is fed into xargs, which uses its standard input as arguments to some command.

List everything not a gif:

ls ^*.gif

This is almost cheating, because it's zsh, and zsh is great. This just means "everything that isn't *.gif". I could have done something like ls * | grep -v '\.gif$', but that's a bit wordy for something this simple, and becomes a problem when I want to feed to something other than ls. (I actually can't rely on ls for a list of filenames, as I have it bound to always ls -l, so I almost always use globs instead.)

Find a link in a log:

grep -Po 'http:[^ ]+' irclogs/network/\#channel.log

-P to use Perl-style regular expressions (so I don't have to figure out what escaping turns on and what it turns off), -o to only print the bit that matches rather than the whole line, and a regex to find "http:" plus whatever follows it up to the first space.

Take and upload a screenshot:

This is actually a script I wrote, but it's shell and sufficiently short enough that it's worth mentioning.

Basically:

1. Take a screenshot with scrot and save it to a temporary file
2. Throw up a standard save dialog, rooted in my screenshot directory, asking for a place to save it
3. Move the screenshot to the requested filename
4. scp the screenshot to the equivalent position on my server
5. Throw up a dialog containing the URL to the new file so I can paste it wherever

3 and 5 are accomplished with zenity, which is a pretty cool tool that can spawn a variety of simple dialogs from the terminal.

I have this script bound to F15 for the entire desktop, or F13 to let me draw a rectangular region to capture. I should probably pngcrush the screenshot, too, but that takes a few seconds, and I was already impatient enough to write a script to take screenshots for me.

Removing some digits and a period from the beginning of filenames:

Someone asked this in response to the last journal. He has a collection of files with timestamps in the filenames, like 123.foo.jpg, and wants to rename them to just foo.jpg.

My first thought is to just use the Perl utility rename:

rename 's{^ \d+ [.]}{}x' *

rename applies a regular expression substitution to filenames. It's like sed for renaming. In this case, it removes a bunch of digits (\d+) followed by a period ([.]) from the beginning (^) of every file in the current directory (*).

However, this person is not on a Unixy system, and so has ready access to neither rename nor Perl itself. Which leaves the glorious hack that is Cygwin, a layer that runs something like Linux on top of Windows. Unfortunately, I don't think Cygwin installs Perl by default, I'm not sure where to get rename outside a package manager or throwing it up myself, and downloading/running a script is more prone to complications than copy/paste. So I came up with a way to do this with stock tools.

for file in *; do $(sed -e 's/^\([0-9]\+\.\)\(.\+\)$/mv \1\2 \2/' <<< $file); done

To peel this apart like an onion:
for file in *; do ...; done will loop through all the files in the current directory (*), assign each one's name in turn to $file, then run the body of the loop.
$(...) runs its contents, then substitutes the output in its place. So $(echo ls) becomes just ls.
command <<< word feeds word to command as input.
sed runs a regex substitution on its input. In this case, it replaces ^\([0-9]\+\.\)\(.\+\)$ with mv \1\2 \2. That is, it changes 123.foo.jpg into mv 123.foo.jpg foo.jpg. $(...) then runs that, renaming a single file, and the for loop repeats for every file in the directory.

two_pi_r has also informed me that zsh comes with a zmv command which works similarly to rename but with wacky zsh syntax.

I'd dig more and/or explain better, but it's late and I was hoping to squeeze out another hour of work. :T

nablopomo09, linux, geeky

Previous post Next post
Up