3

I'm looking to write a bash script that will recursively find all .less files in a directory and create a .css file with the same name in a parent css directory using lessc. Visually it looks like this.

Before:

/css
/less
    a.less
    b.less
/whatever
    /css
    /less
        c.less
        d.less

After:

/css
    a.css
    b.css
/less
    a.less
    b.less
/whatever
    /css
        c.css
        d.css
    /less
        c.less
        d.less

What I've been able to figure out so far is that I can find all the files using:

find . -name *.less

And I can generate individual files using:

lessc foo.less > ../css/foo.css

My problem comes trying to pipe the results of the find into lessc. Per this question I can see how the results of find can be piped into cat, but the same approach isn't working for me with lessc.

In general I'm just having trouble putting the pieces together. Any help would be appreciated.

Thanks.

Edit

This is what worked for me. Thanks @ghoti!

find . -name \*.less -print | sed -rne 's:(.*)/less/([^/]+).less$:lessc & > \1/css/\2.css:p' | sh
Community
  • 1
  • 1
TJ VanToll
  • 12,584
  • 4
  • 43
  • 45

3 Answers3

6

You've got all the ingredients. You just need the recipe. :-)

And there are multiple recipes.

Normally, you can use find's -exec option to execute things based on what it finds. However, you're limited in what you can name things. For example, if you were to use this:

find . -name \*.less -exec lessc {} ../css/{}.css

you would create files in a css directory in the parent from where find is running, and files would be named things like a.less.css, b.less.css, etc. That's not good. You'd need to embed dirname and such into your command, and then things get impossible to manage, with too many quotes and backslashes.

When I have to do pattern-matting AND changes like this, I often resort to pipes. Here's what I'd do:

find . -name \*.less -print | \
  sed -rne 's:(.*)/less/([^/]+).less$:lessc & > \1/css/\2.css:p'

The find gets your files, as you'd expect. Then the sed script turns them into command lines. The output of this is a collection of lessc command lines which you can simply pipe into sh once you've verified that they look right.

Here are the results.

Before:

[ghoti@pc ~/tmp1]$ find . -type f -print
./whatever/less/c.less
./whatever/less/d.less
./less/a.less
./less/b.less
[ghoti@pc ~/tmp1]$ 

Then the test run:

[ghoti@pc ~/tmp1]$ find . -name \*.less -print | sed -rne 's:(.*)/less/([^/]+).less$:cat & > \1/css/\2.css:p'
cat ./whatever/less/c.less > ./whatever/css/c.css
cat ./whatever/less/d.less > ./whatever/css/d.css
cat ./less/a.less > ./css/a.css
cat ./less/b.less > ./css/b.css
[ghoti@pc ~/tmp1]$ 

Then the final run, and results.

[ghoti@pc ~/tmp1]$ find . -name \*.less -print | sed -rne 's:(.*)/less/([^/]+).less$:cat & > \1/css/\2.css:p' | sh
[ghoti@pc ~/tmp1]$ find . -type f -print
./css/b.css
./css/a.css
./whatever/less/c.less
./whatever/less/d.less
./whatever/css/d.css
./whatever/css/c.css
./less/a.less
./less/b.less
[ghoti@pc ~/tmp1]$ 

For my test, I replaced lessc with cat, since less isn't installed on my workstation.

Note that this will only work with simple filenames like the ones in your example. If you have spaces or possibly other "special" characters, it will break.

ghoti
  • 45,319
  • 8
  • 65
  • 104
  • Also just as an fyi for anyone else that happens to see this apparently the ```-r``` option for ```sed``` isn't supported on OS X, but you can substitute it with ```-E``` (http://www.flashesofpanic.com/panic/002596.php). – TJ VanToll Jun 06 '12 at 18:46
  • @TJVanToll - good point; I used the `-r` option in my answer because the question is tagged `linux`. The `-E` option derives from older versions of FreeBSD's sed, on which OSX's sed is based. In newer FreeBSD, sed includes the `-r` option *as well*, for easier interoperability with GNU sed. For anyone else reading this, if you're using an operating system whose `sed` does not include an option for ERE, then you'll have to translate the regex to BRE. Or ask how to do it on StackOverflow. :) – ghoti Jun 06 '12 at 20:02
  • Thanks again. The script is eventually going to have to run on a linux box but I was testing it out on OS X so I thought I'd include that tidbit in case anyone randomly Googles this in the future. – TJ VanToll Jun 07 '12 at 01:35
  • For posterity, `'s:(.*)/less/([^/]+).less$:cat & > \1/css/\2.css:p'`, in basic RE looks like `'s:\(.*\)/less/\([^/][^/]*\).less$:cat & > \1/css/\2.css:p'`. Backreferences (`\1` and `\2`) should work the same in every sed implementation. – ghoti Jun 07 '12 at 03:10
  • Is it possible to get this running in a shell script? I've tweaked it a bit, but nothing seems to work – AJMaxwell Jan 28 '13 at 19:13
  • 1
    @AJMaxwell - sure, it should work in a shell script. If you're having trouble getting your shell script running, I recommend [asking a question](http://stackoverflow.com/questions/ask) tagged for whatever shell you're using, and the tools that are causing difficulties. In your question, be sure to include your existing script and the complete errors you're seeing. – ghoti Jan 30 '13 at 15:34
  • @ghoti - I got the script working. It turns out my installation of `lessc` was pooched. After uninstalling/re-installing a number of packages, I got everything working again :-) – AJMaxwell Feb 01 '13 at 17:15
1

Use a for loop

for file in $(find . -name *.less)
do
    css="${file//less/css}"
    mkdir -p $(dirname "$css")
    lessc "$file" > "$css"
done
k107
  • 15,882
  • 11
  • 61
  • 59
  • Thanks for the help. When I run this I'm getting the following error (once per ```.less``` file). ```../css/./less/FILE_NAME.css: No such file or directory``` – TJ VanToll Jun 06 '12 at 18:48
0

If your .css file is in the D:\demo folder, create a file convert.bat containing:

$ d:
$ cd demo
$ lessc style.less style.css

When you run this file, it will convert .less to .css.

aboger
  • 2,214
  • 6
  • 33
  • 47