Category: Tips

Regular expressions: positive and negative lookaheads and lookbehinds

Do you work with regular expressions sometimes? If you do, and you don’t know about lookbehinds and lookaheads, you are missing out on a fantastic feature. I’m just pasting in something I said to some colleagues. Use your imagination and Google to extract the rest of the blog post 😉

[10/30/14, 10:11:44 PM] Kevin Kaland: man
[10/30/14, 10:11:53 PM] Kevin Kaland: lookaheads and lookbehinds in regex are awesome
[10/30/14, 10:11:58 PM] Kevin Kaland: I can’t believe I survived so long without them
[10/30/14, 10:12:46 PM] Kevin Kaland: you ever done a find/replace to change stuff like




and wound up accidentally with stuff like because you double-replaced?
[10/30/14, 10:13:07 PM] Kevin Kaland: if you use the regex (?<!site\.)nav\. for your find then this can’t happen
[10/30/14, 10:13:52 PM] Kevin Kaland: it won’t match it if it already has the site. in front. it’s called a negative lookbehind. negative meaning “it is not there” and lookbehind meaning that it checks before a particular string. there’s also positive look(ahead|behind)s, which make sure things are there.
[10/30/14, 10:14:08 PM] Kevin Kaland: but that text doesn’t become part of the actual match, which is the nice thing

One SSH key to rule them all: Forward your SSH agent session in 15 seconds

Recently, I used a tool that spoke of “forwarding” my SSH session to the server and thus avoiding needing to copy my private key to the server in order to be able to access Git repositories or other servers where I log in by public key.

If you manage your keys at all, you can immediately see the allure here.

The configuration is ridiculously easy. Put this in your $HOME/.ssh/config file* (Windows users, check PuTTY settings; it can probably do this too).

Host <hostname>

ForwardAgent yes

You can, of course, combine this with other options such as HostName and User.

I tested it with Fill PDF Service:

Host fps


User myusername

ForwardAgent yes

ssh fps
cd /path/to/my/web/root
git pull

(The Git repository is password-protected, and my Git setup uses SSH for authentication by default.)

I got back: Already up to date.

I used to be prompted for my password, but that’s yesterday’s news…quite literally.

Extra tip: If no one else uses your computer, you can put ForwardAgent yes on its own line. This will forward your agent to all servers you connect to. I’m not an SSH expert, but as far as I know, ssh-agent is designed to be extremely secure. The main risk is if someone is using your computer directly, but that applies to most things. SSH Agent sessions are restricted to the current user session via environment variables (so no one can simply switch to you on a server to get access).

It blew me away how easy it is to get this going. 2013 is the year of SSH agent forwarding for me. Hope this helps!

* If the file doesn’t exist, create it. Make sure the permissions on the .ssh directory are 600 (drwx——).

Git: Rebase workflow with remotes involved

Note: I haven’t gone to great pains to make this post beginner-friendly, but if you’re in my situation, you should be able to follow along and understand the problem and how I solve it. Feedback welcome in the comments.


I’ve been trying to stick strictly to a rebase workflow in Git, rather than using git merge.

This has not been without it perils.

However, I’ve found a workflow that works for me as an individual developer using my own feature branch that I push to a remote. Do not use the following workflow for other situations unless you know what you’re doing.

“Why are you doing this?” and such

The problem: After rebasing using the mainline as a base (git rebase master) you can no longer push the rebased branch to a remote since it won’t fast-forward anymore.

Why would you push it to a remote? Because you have untracked/dirty files that need to stay that way locally. In my case, my Drupal settings.php is in the repository – don’t blame me, I don’t control the client’s internal processes – and it must stay dirty for my local site to function. Rebasing is much easier when you don’t have to worry about such files, though, so I keep a second clone of the repository that I use for rebasing.

Pushing my changes from the dirty repository works fine, naturally, since I don’t rebase there.

But once I rebase in the clean repository, how do I push those changes back to the dirty one without causing a merge?

It actually only takes a few commands, and it is pretty safe as long as everything you want to commit to your feature branch is already committed.

The good part (commands!)

This is what I do:

cd /path/to/clean/repository/clone

git pull (remember, I don’t work on this repository, so it will always fast-forward)

git checkout featurebranch

git rebase master

(fix any conflicts and complete the rebase)

git push -f origin featurebranch (this overwrites the remote with what I now have)

cd /path/to/dirty/repository/clone

git checkout master

git branch -D featurebranch (this deletes the local featurebranch branch)

git checkout featurebranch (you may have to use git checkout -b featurebranch origin/featurebranch – I don’t know what differentiates if you have to or not)

Explanation and conclusion

So basically, I’m recreating the local branch from the remote branch. Then I continue developing and rinse/repeat when I need to integrate changes from the mainline or when I’m done with my feature and want to make sure it will fast-forward into the mainline. I finally developed this workflow after finding I always had to merge my feature branch into the mainline in the end; it would never fast-forward. This is because I was running git rebase origin/featurebranch from within the feature branch, since then it would let me push. This basically defeated the purpose of rebasing since it would rebase the new commits from the mainline on top of mine. I wanted mine on top of the new mainline commits.

Hope this helps some poor soul out there who’s in the same boat I was.

UPDATED: Weird fix for CCK fields not appearing with Display Suite (Drupal 6)

Update: Oops, this wasn’t the problem at all. It was actually with the Content Permissions module. Forgot the site was using it! Pretty embarrassing 🙂

I just went through quite a struggle getting my CCK fields to appear on my Display Suite-managed node. I finally succeeded, though, in the end, and thought I would share the solution.

Don’t worry, there isn’t much to read: just disable and re-enable the Node Displays CCK (nd_cck) module found within in the Node Displays Contributions module.

I initially tried disabling and re-enabling all Display Suite-related modules, but that didn’t work. For some reason, however, disabling and re-enabling that specific one did.

Hope this solves someone’s problem!

(To read my full rant, check out this issue comment and the ones below.)

Last-ditch Solution to Non-Working PHP-FPM + Apache Configuration

I had a surreal experience yesterday. I was following online tutorials about setting up Apache + PHP-FPM (for example, this ServerFault question: I’ll let you read that rather than re-hash it.

My goal here is only to share quickly how I actually got this working.

Alright, so you know the part where it says to add the directives:

AddHandler php5-fcgi .php
Action php5-fcgi /fcgi-bin/php5.external

This didn’t work for me no matter what I did. No errors were produced, so I knew that it simply wasn’t executing the Action directive for whatever reason. In checking the Apache 2.2 documentation for Action, I noticed that a MIME type could be given in lieu of an action-type (the php5-fcgi thing). Having exhausted all other options, and knowing that the PHP file was being sent to the browser unprocessed with the MIME type application/x-httpd-php, I decided to add:

Action application/x-httpd-php /fcgi-bin/php5.external

to my configuration. And, much to my shock, it actually worked!

So, if you find yourself as frustrated with setting up Apache + PHP-FPM as I was, I hope this tip may ease your suffering.

Linux tip – regular expression find and replace in all files in a directory

As you may have seen me tweet, I’ve been looking for a way to do this. I didn’t want to manually change my Apache configuration to reflect my new internal IP address. After some Internet searching, I stumbled across this gem:

find . -name '[^.]*' | xargs perl -pi -e 's/192\.168\.1\.3/192\.168\.0\.3/g'

I adapted it to this for my task of replacing IP addresses. The first set of numbers is the old one (don’t delete the backslashes) and the second set is the new one.

This command assumes all files in the directory are configuration files and do not start with a dot.


Update: According to a commenter, sed -i 's/thisip/thatip/g' * should also work. I didn’t try that since I thought it wouldn’t work with multiple input files.

Cloning Content Types in D7 – Errata

Update: You may also want to check out the Bundle Copy module. I haven’t tried it yet, so let me know in the comments if it works!

Recently, I read the Stanford Tech Commons article on Cloning a Content Type in D7 at It proposes a simple but (mostly) effective way  to wind up with cloned content types in Drupal 7. Summarized:

How-to: Create Drupal development sites in Quickstart

Yesterday, I felt like reviewing some patches, so I fired up my Quickstart-based virtual machine and set about creating some Drupal development sites. I realized I first had to create Drush Make files to get the proper development versions installed. So I did that. However, I also realized that, despite cloning the code via Git and checking out a particular branch, the Git clone was not actually a Git repository. This is because Drush Make requires the –working-copy switch in order to do this. I’ve posted a workaround on the Quickstart issue queues. This post mostly serves as pointers to a couple things:

Feeds CSV Importer Sources: Caveat Amplificator

I had an interesting experience with Feeds and Feeds Tamper today. I wasn’t able to get a comma-separated set of words to turn into multiple tags no matter what I tried. I was using the Explode plugin that comes with Feeds Tamper and set the delimiter as a comma and the limit to 1. My research indicated this should work, but it didn’t.

There were two problems that were so counterintuitive, the urge to blog about them came over me. Disclaimer: I admit I’ve never properly read the Feeds documentation; I figured setting up a node importer based off a CSV parser would be pretty easy.

The solution is extremely simple:

When setting up your CSV importer and specifying Source column names, do not use spaces or uppercase letters. This means you need to avoid it in your CSV file as well.

That’s all! Instead of Body Text, call it body_text. What happens is that by the time Feeds Tamper receives your Feeds field, the data structure it uses to store them contains lowercase (and probably space-free) versions of whatever you had as the Source column names back in your updater. Using my tip ensures that these will be the same, and Feeds Tamper will be happy.

Have fun tampering.

P.S. The Latin in the title hopefully means, “Developer beware.”

Code Snippet – Open Link in New Window in Drupal 7

Although this is not recommended, I’ve seen the question mentioned in the topic come up a couple times over the last few weeks:

How do I get my links to open in a new window or tab in the user’s browser in Drupal 7?

I’ve heard that the target=”_blank” method is once again valid in HTML 5…however, it will still make your HTML fail validation under other specifications. The way around this is to use JavaScript.

Copy and paste this snippet into the appropriate area of your site – usually a JavaScript file in your theme. When you want to make a link open in a new window or tab, instead of target=_blank, add rel=”external”.

(function ($) {
Drupal.behaviors.externalLink = {
attach: function (context, settings) {
$('a[rel="external"]', context).attr("target", "_blank");

The reason this workaround is OK to use is that it does a little something called separating presentation from behavior. In other words, you aren’t relying on the browser to take a particular action because of the HTML itself; you’re telling it what action to take in the JavaScript.