Combining @Ramnath and @Tommy's answers allowed me to find an approach that works in base R for one or more columns.
Basic usage:
> df = data.frame(
+ id=1:3, foo=c('a|b','b|c','c|d'),
+ bar=c('p|q', 'r|s', 's|t'), stringsAsFactors=F)
> transform(df, test=do.call(rbind, strsplit(foo, '|', fixed=TRUE)), stringsAsFactors=F)
id foo bar test.1 test.2
1 1 a|b p|q a b
2 2 b|c r|s b c
3 3 c|d s|t c d
Multiple columns:
> transform(df, lapply(list(foo,bar),
+ function(x)do.call(rbind, strsplit(x, '|', fixed=TRUE))), stringsAsFactors=F)
id foo bar X1 X2 X1.1 X2.1
1 1 a|b p|q a b p q
2 2 b|c r|s b c r s
3 3 c|d s|t c d s t
Better naming of multiple split columns:
> transform(df, lapply({l<-list(foo,bar);names(l)=c('foo','bar');l},
+ function(x)do.call(rbind, strsplit(x, '|', fixed=TRUE))), stringsAsFactors=F)
id foo bar foo.1 foo.2 bar.1 bar.2
1 1 a|b p|q a b p q
2 2 b|c r|s b c r s
3 3 c|d s|t c d s t