Tag Archive: zsh


Zsh glob qualifiers ftw!

I have a FreeBSD 7.1 server, a couple of macs, a windows machine, and an ubuntu machine. I need to share files between them all. I could try to get NFS for windows working, but it seemed to me that using Samba was a good way for me to allow all of the machines (Even the windows one!) have access to the storage I have on my “server”. I’ve been working with BSD, Linux and Windows for a long time, and I still get emails to thank me for that old Linux From Scratch hint I wrote that describes how to set up printing with samba and cups. I thought this would be a cinch. Was it? Of course not haha. It turns out that even though my user had access to mount the share, I had mounted it in FreeBSD owned by user root, group wheel with permission set to 755. My user is in the wheel group and I would like the wheel group to have write permission, so I remounted it with the following line in /etc/fstab:

/dev/ad8s1  /mnt/sambashare msdosfs rw,large,-m=775,-g=wheel 0 0

the large is there because it’s a 500GB drive.

And now, all is well. I can mount the share and I have read write access! I can create and delete a test folder… so that’s it! Right? Wrong. There’s an old windows directory on there that I need to delete, but OS X says I haven’t got enough permission to do that. Oh no!

Well how am I supposed to go digging through some directory tree and find the files that I don’t have permission to delete? I bet there’s a unix command that can help me… (actually I know there is – it’s called find, and find is great…but… I love zsh and I love finding fun reasons to use its features!)  Since I know ls will list my files… and ls -al will tell me the permissions… why not just say “ls -al everything i dont have write permission to”?

ls -al *(^I)

The stuff that is between the parentheses is called a glob qualifier. Glob qualifiers let you ask zsh to give you back more specific information. In this case, the capital I means group writable files and the ^ is used to negate the qualifier. If you try to translate this command line into english it would say something like, “list the names and permissions of all of the files in the current directory, even the hidden ones, that are not group writable.”

You may be thinking, “Wait… did you say all files in the current directory?” and if you are thinking that – you’re right. The above command only lists the files in the current directory, so it is not very useful right now.  I need to look in my mounted drive, which is on /mnt/sambashare, so how about this command line?

ls -al /mnt/sambashare/*(^I)

That is close… but not quite there. It’s only showing files in that directory, and I need to look into all the directories that might be in there. Since all the files and directories in that directory are group writable, this gives me:

zsh: no match

So how can I tell ls to look into directories too? That’s easy! I can use the recursive globbing operator, **

Now the command line looks like this:

ls -al /mnt/sambashare/**(^I)

But that STILL doesn’t go all the way into directories I need it to. This is getting crazy now isn’t it? It turns out that to look into directories, even directories that are inside other directories, we cannot simply use the recursive globbing operator, we need to append a /* as well, which makes the command line:

ls -al /mnt/sambashare/**/*(^I)

But now… oh no! We’re still not there! This returned a list including some files that ARE group writable! Why don’t I just run chmod -R  and end this agony already? (Is that what you’re thinking? haha well the answer is… I want to know exactly what files I can’t delete before I take further action). It seems that the ls command is going into directories and showing me things I didn’t ask for. I have a hunch that it’s because ls is doing some recursion of its own… (don’t ask me why, it’s just a hunch) so I’m going to add the -d option to the ls command, making the command line….(drum roll please):

ls -ald /mnt/sambashare/**/*(^I)

….and my hunch was correct! So now I have my list of all files in my share that are not group writable. It might seem like a lot of time and effort, but for a seasoned zsh user this is nothing! I did have to consult the zsh man page to find the glob qualifier for “group writable files”, but that didn’t take long. In the next article I’ll continue with this scenario, and tell you if I ever did manage to get those files deleted :) Stay tuned… if you dare!

A friend of mine keeps an OpenBSD box up so that we can use it for a shell if we need one (it’s come in handy on more than one occasion, let me tell you!). I was logged in the other day and wanted to add a package. My zsh startup files make it a rather simple process most of the time, partly because of the fact that I set the PKG_PATH variable dynamically. Part of that dynamic value is taken from the OSTYPE variable.

To make a long story short(er) I couldn’t add a package via pkg_add -i, and the reason was that OSTYPE was returning openbsd4.1 even though uname reported “OpenBSD isis.beyondnormal.net 4.2 GENERIC#375 i386″. The local mirror I use doesn’t keep packages around from old releases, so that was part of why this failed. So why was OSTYPE returning 4.1 even though it was OpenBSD 4.2? The answer is that OSTYPE is set at compile time.

Apparently, after upgrading the OS, the packages from OpenBSD 4.1 will work ok with OpenBSD 4.2 (at least in the case of zsh-4.2.6). All I needed to do to get OSTYPE to be correct was to uninstall the zsh package and install the package that was released with OpenBSD 4.2. Now let’s see if we remember when OpenBSD 4.3 is released…

Sed on Mac OS X 10.5 Leopard

Imagine you’re a PHP developer that uses OS X. You’re given 50+ php files that all have a line that needs to be changed. 50 files, one change? Hmmm sounds like maybe I could use automator to do this! Well I don’t know how to use automator ;) Luckily, I’m a unix geek, and even more luckily, OS X has a fairly strong unix back end and a great terminal emulator. So how does that help? Well if you’re a unix geek you know that this sort of problem just screams “SED! use SED! this is what SED IS FOR!” And it’s true. This is where sed is great.

Say you want to change this line:

 $config_file = $_SERVER['DOCUMENT_ROOT']."/dev/config.php";

To this:

 $config_file = "dev/config.php";

As a matter of fact… wait a minute! I don’t want to just change that line… no I want to be able to show what I’ve changed, so that the next person who looks at this can see what it used to say. In this instance it’s also important to show the work I’ve done because I’m making changes to someone else’s work. So what I want to do is comment out the line and then add a new line with my change underneath. Sounds a bit more complicated right? Well it’s not really – unless you’re using OS X (you’ll see why in a minute *sigh*). The goal is to end up with this:

 //$config_file = $_SERVER['DOCUMENT_ROOT']."/dev/config.php";
 $config_file = "dev/config.php";

On the linux machine I keep around the office the command to perform this change on the all php files in the current directory looks like this ( I had to split the line to fit on the site):

sed -i .bak "s/^\$config_file = \
\$_SERVER\['DOCUMENT_ROOT'\]\.\"\/dev\/config\.php\"\;/\/\/\
\$config_file = \$_SERVER\['DOCUMENT_ROOT'\]\.\"\/dev\/config\.php\"\;\
\n\$config_file = \"dev\/config\.php\"\;/g" *.php

The breakdown of the above command is as follows:

sed | the sed command

-i .bak | the -i option means do this change “in place” and copy the original file to originalname.bak

| we use the double quote here to start the sed command because there are single quotes in the command

s/^\$config_file =
\$_SERVER\['DOCUMENT_ROOT'\]\.\”\/dev\/config\.php\”\;/\/\/\$config_file
= \$_SERVER\['DOCUMENT_ROOT'\]\.\”\/dev\/config\.php\”\;\n\$config_file = \”dev\/config\.php\”\;/g
| this is the magic of sed. it looks for the line we want to change, adds // to the beginning of it, adds a new line after it, and then adds the text that we want after the new line. Amazing isn’t it?

| the command ends with the double quote

and, finally:

*.php | represents all of the files that end in .php in the current directory.

Now if you thought that was a mess, check out how that command needs to look in order to work on OS X (10.5.1, possibly other versions)

sed -i .bak "s/^\$config_file = \
\$_SERVER\['DOCUMENT_ROOT'\]\.\"\/dev\/config\.php\"\;/\/\/\
\$config_file = \$_SERVER\['DOCUMENT_ROOT'\]\.\"\/dev\/config\.php\"\;\
\\"$'\n'"\
\$config_file = \"dev\/config\.php\"\;/g" index.php

The crazy part here is the need to use \\”$’\n’”\

What that represents is an escaped version of the literal newline character. With the version of sed currently in OS X, that is the only way (that I could find) to add a newline character with sed. Now you know too.

So that concludes this edition of mac unix geekery… until next time…