Preventing Wildcard Expansion / Globbing in Shell Scripts

I wanted to pass a string containing a wildcard to a shell script and not worry about the shell automatically globbing it for me.

For example, lets consider a simple script, globme.sh

#!/bin/bash

echo "1[${1}]"
echo "@[${@}]"
echo

which is in a directory containing:

globme.sh
notes.txt
phonebook.csv
todo.txt

Let’s see how the script reacts to different inputs. First, lets look at what it does without any wildcards:

$ ./globme.sh Not Wildcards
1[Not]
2[Wildcards]
@[Not Wildcards]

It works as one might expect, the 2nd space-separated argument passed to the script is placed into $2. If you expect a certain value to always be at $2, no matter what, you might be surprised when you call the script with something like:

$ ./globme.sh *.txt red

and find out that $2 is not “red”, but in fact the name of a file in your current directory:

$ ./globme.sh *.txt socket
1[notes.txt]
2[todo.txt]
@[notes.txt todo.txt socket]

But note the fact that, if your wildcard does not match set of files in your current directory, it will be passed to the script as-is:

$ ./globme.sh *.py socket
1[*.py]
2[socket]
@[*.py socket]

Well this is not good. The same file and same input acts two different ways based what directory you’re in. Of course, one solution would be to always remember to enclose your arguments in quotes (single or double):

$ ./globme.sh '*.txt' socket
1[*.txt]
2[socket]
@[*.txt socket]

$ ./globme.sh "*.txt" socket
1[*.txt]
2[socket]
@[*.txt socket]

But what if you didn’t want to require this? What if there were no circumstances under which you wanted the shell to expand wildcards for you? I’ll bring you slowly through the steps you might have taken had you not found this tutorial, but if you’re in a rush, you can skip to the end to my favorite solution.

After some searching, you may have found out that there is a shell option (f) that you can set that will disable this behavior. To do this, simply call set -f, as in:

$ ./globme.sh * *
1[globme.sh]
2[notes.txt]
@[globme.sh notes.txt phonebook.csv todo.txt globme.sh notes.txt phonebook.csv todo.txt]

$ set -f

$ ./globme.sh * *
1[*]
2[*]
@[* *]

So you could always call set -f before every globme.sh, but that gets tedious.

Okay, so then you could alias it by adding the following to your .bashrc / .bash_profile file:

alias globme='set -f; /path/to/globme.sh'

Which is better, since you don’t have to remember to call another program before globme, and even if you remember to call another program, you don’t have to remember the program or syntax.

But now you run into the problem where your shell option persists after your command. So while you might expect that cat * prints the contents of each file in your current directory, you’ll get an error:

$ cat *
cat: *: No such file or directory

This problem is exacerbated by the fact that subshells that spawn from your current shell inherit those options. So shell scripts, etc. that expect and rely a certain behavior will not function correctly.

Okay, so one solution would be to remember to clear the shell option after each time using set +f. This would work, but it’s terribly annoying to have to remember.

If you’re wondering whether you can modify your alias to also clear the option, perhaps like:

alias globme='set -f; /path/to/globme.sh; set+f;'

You’ll be disappointed to learn that you cannot. Your arguments are added after the alias, so you’d get something like

$ set -f
$ /path/to/globme.sh
$ set+f
$ YourFirstArgument YourSecondArgument ...

So maybe you’ll drop the alias and define a function in your .bashrc / .bash_profile to handle this for you. Maybe something like

globme()
{
    set -f
    /path/to/globme.sh
    set+f
}

You’d be further disappointed when you found out that the shell expanded your wildcards before they were passed to the function, so you’d be setting the -f option too late!

Working (but not optimal solutions)

My first solution was to edit my script to respawn the shell upon exiting. Simply put, I would use the shell’s exec command to exit into a new instance of the shell. Specifically, I would add

exec /bin/bash

or to generalize it (perhaps unnecessarily),

exec ${SHELL}

at the end of the program.

The problem with this solution is that you would lose any environment variables you had set. For example:

$ TEST="Testing"
$ echo $TEST
Testing
$ exec ${SHELL}
$ echo $TEST
(blank line)

This is not expected behavior unless you explicitly respawn your shell instance. Also, consider that your script may have multiple exit points (perhaps erroring out at various points, etc) — in that case you’d need to add the line at every exit point. Further complicating the problem is that by doing this, you’re losing the exit-code of your program. Whereas before you could have used return 1 to indicate an error (which can be viewed by echo $? as the next command after your program), now your exec will have to be in it’s place.

So next, my almost-done solution integrated both aliases and functions:

alias globme='set -f; g'
g(){ /path/to/globme.sh "$@"; set +f; }

Here, the alias allows us to set the shell option first, so the wildcards are not expanded. Then, the function g() is called, which is simply globme, with “$@” as the argument (will get to that in a second), followed by the command to clear the shell option.

This works. Perfectly. To check, you could type

echo $-

and if you see an ‘f’, the option is set (remember, it’s set with -f and cleared with +f) — if you don’t, it’s cleared.

The “$@” expands to the arguments that were passed to the function g — which in this case, are the arguments you passed at the prompt!

Cut to the chase!

Lastly, I realized that it might be a little annoying to have a function and an alias for each script that you wished behave like this. To generalize our lines, I changed them to the following:

reset_expansion(){ CMD="$1"; shift; $CMD "$@"; set +f; }
alias globme='set -f; reset_expansion /path/to/globme.sh'

which basically does the same thing, but it allows you to reuse the reset_expansion() function. For example if you had a second script that you wanted to behave the same way named newscript, you would only need to add the following to your .bashrc / .bash_profile:

alias newscript='set -f; reset_expansion /path/to/newscript'

And that’s all she wrote. Now, the bash philosophers out there will will say something along the lines of “Just enclose wildcard arguments in quotes”, which has a lot of merit, but I’d rather add these lines and not worry about it. Enjoy!

One comment so far, add another

Colored Output in C/C++ (Part 3)

This is part 3 of a 3 part series in printing in color in C/C++

(Continued from Part 2)

cprintf.h

////////////////////////////////////////////////////////////////////////////////////////////////////
//
// (C)2011 Edwards Research Group
// You are licensed to use this work under a CC-BY-SA License.
// See: http://blog.edwards-research.com/about/
//      http://creativecommons.org/licenses/by-sa/3.0/us/
//
////////////////////////////////////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <stdarg.h>

enum CP_ColorType {BLACK   = 0,
                   RED     = 1,
                   GREEN   = 2,
                   YELLOW  = 3,
                   BLUE    = 4,
                   MAGENTA = 5,
                   CYAN    = 6,
                   WHITE   = 7,
                   UNDEF   = -1};

enum CP_AttrType  {NONE    = 0,
                   BOLD    = 1,
                   DIM     = 2,
                   UNDERLINE = 4,
                   BLINK   = 5,
                   REVERSE = 7};

class ColorSet
{
public:
    CP_ColorType _fg;
    CP_ColorType _bg;
    CP_AttrType  _attr;

    // Default Constructor
    ColorSet(){ set(UNDEF,UNDEF,NONE); }

    // 1 param constructor
    ColorSet(CP_ColorType fg){ set(fg,UNDEF,NONE); }

    // 2 param constructors
    ColorSet(CP_ColorType fg, CP_AttrType attr){ set(fg,UNDEF,attr); }
    ColorSet(CP_ColorType fg, CP_ColorType bg) { set(fg,bg,NONE); }

    // 3 param constructor
    ColorSet(CP_ColorType fg, CP_ColorType bg, CP_AttrType attr){ set(fg,bg,attr); }

    void set(CP_ColorType fg, CP_ColorType bg, CP_AttrType attr)
    {
        _fg   = fg;
        _bg   = bg;
        _attr = attr;
    }

    // Cool Wrapper
    void cprintf(const char * fmt, ...)
    {
        va_list args;
        va_start(args, fmt);

        cp_init();
        vprintf(fmt, args);
        cp_rst();

        va_end(args);
    }

private:
    void cp_init(void)
    {
        if(_bg != -1){
            printf("%c[%d;%d;%dm",27,_attr,(30+_fg),(40+_bg));
        }
        else{
            printf("%c[%d;%dm",27,_attr,(30+_fg));
        }
    }

    void cp_rst(void)
    {
        printf("%c[%dm", 27, 0);
    }
};

cprintf_test.cpp

////////////////////////////////////////////////////////////////////////////////////////////////////
//
// (C)2011 Edwards Research Group
// You are licensed to use this work under a CC-BY-SA License.
// See: http://blog.edwards-research.com/about/
//      http://creativecommons.org/licenses/by-sa/3.0/us/
//
////////////////////////////////////////////////////////////////////////////////////////////////////

#include <stdio.h>

#include "cprintf.h"

int main(void)
{
    ColorSet alert(RED, BOLD);
    ColorSet warn(YELLOW, UNDERLINE);

    printf("\n");
    printf("Unformatted...\n");
    alert.cprintf("THIS IS AN ALERT!\n");
    warn.cprintf("THIS IS A WARNING.\n");
    printf("\n");

    int i,j;
    for(i=0; i<8; i++)
    {
        for(j=0; j<8; j++)
        {
            if(j == 3 | j == 6){ continue; }
            ColorSet((CP_ColorType)i, (CP_AttrType)j).cprintf("FG=%d,A=%d", i, j);
            printf("    ");
        }
        printf("\n");
    }
    printf("\n");

    warn.cprintf("Much later, I can simply use warn without having to lookup the style I last used.\n");

    // Example Showing real-time definition and overloaded constructors
    ColorSet(YELLOW, RED).cprintf("Yellow on Red...");
    printf("\n");
    ColorSet(BLUE, REVERSE).cprintf("Reversed Blue...");
    printf("\n");

    return 0;
}

Which looks like this in gnome-terminal:

Leveraging some of the benefits of c++, I was able to streamline some of the ColorSet construction that took up an annoying amount of lines with the struct style.

I also changed the #defines to an enum to make the compile-time checking more intelligent.

Lastly, the overloaded constructors allow me to use a 2-parameter constructor for both (FOREGROUND, BACKGROUND) and (FOREGROUND, ATTRIBUTE) style.

Now, if I were to make this production code, I’d hide my member variables, expose getter and setter interfaces (if necessary), re-prefix the enum’s to avoid conflicts, and separate out the implementation into a .cpp file — but this was really just a proof-of-concept thing.

Leave the first comment

Colored Output in C/C++ (Part 2)

This is part 2 of a 3 part series in printing in color in C/C++

(Continued from Part 1)

In order to be able to specify a “color set”, and avoid having to define parameters I didn’t want, my next iteration was to move to a structure-based approach:

cprintf.h

////////////////////////////////////////////////////////////////////////////////////////////////////
//
// (C)2011 Edwards Research Group
// You are licensed to use this work under a CC-BY-SA License.
// See: http://blog.edwards-research.com/about/
//      http://creativecommons.org/licenses/by-sa/3.0/us/
//
////////////////////////////////////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <stdarg.h>

#define CLR_BLACK   0
#define CLR_RED     1
#define CLR_GREEN   2
#define CLR_YELLOW  3
#define CLR_BLUE    4
#define CLR_MAGENTA 5
#define CLR_CYAN    6
#define CLR_WHITE   7

#define ATTR_NONE       0
#define ATTR_BOLD       1
#define ATTR_DIM        2
#define ATTR_UNDERLINE  4
#define ATTR_BLINK      5
#define ATTR_REVERSE    7

typedef struct{
    int fg;
    int bg;
    int attr;
} cset_t;

cset_t * cset_init(cset_t * cs)
{
    cs->fg = -1;
    cs->bg = -1;
    cs->attr = -1;
    return cs;
}

cset_t * cset_setfg(cset_t * cs, int fg)
{
    cs->fg = fg;
    return cs;
}

cset_t * cset_setbg(cset_t * cs, int bg)
{
    cs->bg = bg;
    return cs;
}

cset_t * cset_setattr(cset_t * cs, int attr){
    cs->attr = attr;
    return cs;
}

void cprint_init(cset_t * cs)
{
    if(cs->bg != -1){
        printf("%c[%d;%d;%dm",27,cs->attr,(30+cs->fg),(40+cs->bg));
    }
    else{
        printf("%c[%d;%dm",27,cs->attr,(30+cs->fg));
    }
}

void cprint_rst(void)
{
    printf("%c[%dm", 27, 0);
}

int cprintf(cset_t * cs, char * fmt, ...)
{
    va_list args;
    va_start(args, fmt);

    cprint_init(cs);
    vprintf(fmt, args);
    cprint_rst();

    va_end(args);
}

cprintf_test.c

////////////////////////////////////////////////////////////////////////////////////////////////////
//
// (C)2011 Edwards Research Group
// You are licensed to use this work under a CC-BY-SA License.
// See: http://blog.edwards-research.com/about/
//      http://creativecommons.org/licenses/by-sa/3.0/us/
//
////////////////////////////////////////////////////////////////////////////////////////////////////

#include <stdio.h>

#include "cprintf.h"

void main(void)
{
    // Example Showing Individual Configuration
    cset_t alert;
    cset_init(&alert);
    cset_setfg(&alert, CLR_RED);
    cset_setattr(&alert, ATTR_BOLD);

    // Example Showing Cascaded Configuration
    cset_t warn;
    cset_setattr(cset_setfg(cset_init(&warn), CLR_YELLOW), ATTR_UNDERLINE);

    printf("Unformatted...\n");
    cprintf(&alert, "THIS IS AN ALERT!\n");
    cprintf(&warn,  "THIS IS A WARNING.\n");
    printf("\n");

    cset_t loop;
    cset_init(&loop);
    int i,j;
    for(i=0; i<8; i++)
    {
        for(j=0; j<8; j++)
        {
            if(j == 3 | j == 6){ continue; }
            cset_setfg(&loop, i);
            cset_setattr(&loop, j);
            cprintf(&loop, "FG=%d,A=%d", i, j);
            printf("    ");
        }
        printf("\n");
    }
    printf("\n");

    cprintf(&warn, "Much later, I can simply use &warn without having to lookup the style I last used.\n");

    return;
}

Which turned out like this (in PuTTY):

I liked this solution better than my first. Here I was able to define program-wide color schemes (e.g. alert, warn) and simply reference them on future calls to cprintf(). I could have also added a one-time-use “constructor” that would return a static pointer to a structure of type cset_t based off 3 integers for inline coding.

Something like:

cset_t onu;

cset_t * cset(int fg, int bg, int attr)
{
    onu.fg = fg;
    onu.bg = bg;
    onu.attr = attr;
    return &onu;
}

So that I could also do something like the following in my main program:

cprintf(cset(CLR_YELLOW, CLR_RED, ATTR_NONE), "Yellow on Red...");

As for my C version, this is where I left it — my next iteration was in C++.

Continue to Part 3.

Leave the first comment

Colored Output in C/C++ (Part 1)

This is part 1 of a 3 part series in printing in color in C/C++

So you wanna do stuff like this from your C/C++ project…

Well I did too, and here’s a little write-up on my journey.

Using the simple escape-code method (described here, here), I wrote the following simple program.

cprintf.h

////////////////////////////////////////////////////////////////////////////////////////////////////
//
// (C)2011 Edwards Research Group
// You are licensed to use this work under a CC-BY-SA License.
// See: http://blog.edwards-research.com/about/
//      http://creativecommons.org/licenses/by-sa/3.0/us/
//
////////////////////////////////////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <stdarg.h>

#define CLR_BLACK   0
#define CLR_RED     1
#define CLR_GREEN   2
#define CLR_YELLOW  3
#define CLR_BLUE    4
#define CLR_MAGENTA 5
#define CLR_CYAN    6
#define CLR_WHITE   7

#define ATTR_NONE       0
#define ATTR_BOLD       1
#define ATTR_DIM        2
#define ATTR_UNDERLINE  4
#define ATTR_BLINK      5
#define ATTR_REVERSE    7

void cprint_init(int fg, int attr, int bg)
{
    if(bg != -1){
        printf("%c[%d;%d;%dm",27,attr,(30+fg),(40+bg));
    }
    else{
        printf("%c[%d;%dm",27,attr,(30+fg));
    }
}

void cprint_rst(void)
{
    printf("%c[%dm", 27, 0);
}

int cprintf(int fg, int a, int bg, char * fmt, ...)
{
    va_list args;
    va_start(args, fmt);

    cprint_init(fg,a,bg);
    vprintf(fmt, args);
    cprint_rst();

    va_end(args);
}

cprintf_test.c

////////////////////////////////////////////////////////////////////////////////////////////////////
//
// (C)2011 Edwards Research Group
// You are licensed to use this work under a CC-BY-SA License.
// See: http://blog.edwards-research.com/about/
//      http://creativecommons.org/licenses/by-sa/3.0/us/
//
////////////////////////////////////////////////////////////////////////////////////////////////////

#include <stdio.h>

#include "cprintf.h"

void main(void)
{
    printf("Unformatted...\n");
    // Example with
    cprintf(4,1,-1,"Blue Bold...\n");
    cprintf(CLR_GREEN,ATTR_UNDERLINE,-1,"Green Underlined...\n");
    cprintf(CLR_YELLOW,ATTR_NONE,CLR_RED,"Yellow on Red...");
    printf("\n\n");

    int i,j;
    for(i=0; i<8; i++)
    {
        for(j=0; j<8; j++)
        {
            if(j == 3 | j == 6){ continue; }
            cprintf(i,j,-1,"FG=%d,A=%d", i, j);
            printf("    ");
        }
        printf("\n");
    }
    printf("\n");
    return;
}

NOTE: These will not work by simply copying and pasting, you must replace/insert the escape sequence as noted in the above linked references.

The above graphic is the program running in PuTTy via an SSH session. This is what the program looks like in gnome-terminal:

Well, this worked. But I didn’t really like having to specify all of the parameters (foreground, background, attributes) every time if all I wanted was the foreground changed. Also, this didn’t give me an easy way to define a color set, and then use that for the rest of the program. For example, it wasn’t easy to define an “alert” color set of, say, red bold text, and use that throughout.

My next step was to address this.

Continue to Part 2.

Leave the first comment

MATLAB / Octave Compatibility

I use Octave on my Linux-based workstations (Office, Home Laptops, Netbook, Server) and MATLAB on my Windows-based home desktop.

I’m currently writing up a (incomplete, to be sure) list of differences between MATLAB and Octave scripts — not so much as a comprehensive comparison but more as a stepping stone to automatic porting / validating that I want to do.

In the meantime, I figured I’d post my most frequently used compatibility functions. These functions add functionality to MATLAB that already exists in Octave. I don’t have many of these, and usually modify the code to be valid on both interpreters. There are a few exceptions, however, where I’ve become accustomed to specific syntax of Octave and would rather add the functionality to MATLAB than change my code.

Yes, these functions are simple. Yes, you could probably write most of these functions yourself. The idea is not to present solutions to difficult problems, but to provide you with a compatibility library that you can use to make porting scripts less painful.

As I get some more time, I’ll add some more of these, but here are my three most frequently used functions:

printf.m

function len = printf( fmt, varargin )
    if nargin == 0
        len = fprintf(1, fmt);
    else
        len = fprintf(1, fmt, varargin{:});
    end
end

rows.m

function r = rows(M)
    r = size(M,1);
end

columns.m

function c=columns(M)
    c = size(M,2);
end
Leave the first comment

Do It Yourself: Whiteboard (Part 3 – Wrap-up)

Continued from Part 1, Part 2.

So the whiteboard project is pretty much complete. The 4×3 foot sheet of poly arrived a few days after I ordered it.

A few days later, I made a trip over to my local home improvement store seeking out some thinner MDF. Unfortunately, I couldn’t find any — the only MDF I found was way too thick — so I went with a 4×4 half-inch sheet of birch plywood that I had them cut down to 4×3.

A few cans of white primer spray paint later, I had a pretty decent backer board for my new whiteboard. The rigidity of the plywood really helped achieve that flat surface I was looking for, and the birch veneer on the plywood allowed me to spray paint it relatively easily, without needing too many coats. The reverse of the plywood also looks much better than the face of most other plywood.

Once the spray paint dried, I lined up the poly and plywood and drilled 1/4 inch holes about 2 inches in from each corner. Inside the holes, sandwiched by flat washers, I slipped a 1 inch hex bolt and fastened it with a nut. I bought 6 bolt/nut sets, but 4 seems sufficient so far. Voila you have your whiteboard.

Now you’ll notice I don’t have the full-size grid behind the poly like I previously described. Instead, I only have a 8.5 x 11 sheet of paper towards the top right corner. That’s because I haven’t yet found a place that will print a larger poster-size sheet inexpensively. I figured I’d try Kinkos, but the guy told me it was like $7.50 per square foot. So for a 12 sq.ft. sheet, it would end up costing me more than the poly and plywood combined! So in the meantime, I spray painted one side of the plywood and it works perfectly. If I were to do this again, I’d probably hunt out white melamine — it’d give me the sturdiness I was looking for and eliminate the need to spray paint one side.

So I’m still hunting out a place to print cheap black and white posters — I guess if I come up fruitless I could align and tape multiple 8.5×11 sheets together, but that’s more hassle than it’s worth in my opinion.

EDIT: I guess I misheard the guy at Kinkos Fedex Office, it turns out they only wanted $0.75 per square foot — so my grid print (full-size PDF available here) only cost $9 + tax. I updated the cost breakdown accordingly.

Lastly, I’ll try to sum up my costs:
Poly + shipping (~50)
Birch Plywood (~25)
2 Cans Spray Paint (~8)
6 Bolts / 6 Nuts, 12 Washers (~2)
Grid Print (~10)
Total: ~$95

The whiteboard rocks — I enjoyed building it — and I hope this will help someone else with their own whiteboard construction project.

8 comments so far, add yours

Gedit highlights Octave .m files as Objective-C Sources

I’ve had this problem since Fedora 11, and it still persists in Fedora 14. Previously, I’ve deleted the objc.lang file as suggested in this thread, however, I’ve recently switched over to the following method:

1. Close all gedit instances
2. Edit the mime-type definition file:

sudo vi /usr/share/mime/packages/freedesktop.org.xml

3. Comment out the objective c mime-type definition by adding a line above line 25666 containing

<!--

and a line above what is now line 25717 containing

-->

.

The end result should look like:

...
    <glob pattern="*.not"/>
  </mime-type>
  <!--
  <mime-type type="text/x-objcsrc">
    <comment>Objective-C source code</comment>
    <comment xml:lang="ar">ﺶﻓﺭﺓ ﻢﺻﺩﺭ ﺎﻠﻫﺪﻓ-C </comment>
    <comment xml:lang="be@latin">Kryničny kod Objective-C</comment>
    <comment xml:lang="bg">Изходен код — Objective C</comment>
    <comment xml:lang="ca">codi font en Objective-C</comment>
    <comment xml:lang="cs">Zdrojový kód v Objective-C</comment>
    <comment xml:lang="da">Objektiv C-kildekode</comment>
    <comment xml:lang="de">Objective-C-Quelltext</comment>
    <comment xml:lang="el">πηγαίος κώδικας Objective-C</comment>
    <comment xml:lang="en_GB">Objective-C source code</comment>
    <comment xml:lang="eo">fontkodo en Objective-C</comment>
    <comment xml:lang="es">código fuente en Objective-C</comment>
    <comment xml:lang="eu">Objective-C iturburu-kodea</comment>
    <comment xml:lang="fi">Objective-C-lähdekoodi</comment>
    <comment xml:lang="fr">code source Objective-C</comment>
    <comment xml:lang="ga">cód foinseach Objective-C</comment>
    <comment xml:lang="he">קוד מקור של Objective-C</comment>
    <comment xml:lang="hu">Objective-C forráskód</comment>
    <comment xml:lang="id">Kode program Objective-C</comment>
    <comment xml:lang="it">Codice sorgente Objective-C</comment>
    <comment xml:lang="ja">Objective-C ソースコード</comment>
    <comment xml:lang="kk">Objective-C бастапқы коды</comment>
    <comment xml:lang="ko">Objective-C 소스 코드</comment>
    <comment xml:lang="lt">Objective-C pradinis kodas</comment>
    <comment xml:lang="lv">Objective-C pirmkods</comment>
    <comment xml:lang="ms">Kod sumber Objective-C</comment>
    <comment xml:lang="nb">Objective-C-kildekode</comment>
    <comment xml:lang="nl">Objective-C-broncode</comment>
    <comment xml:lang="nn">Objective-C-kjeldekode</comment>
    <comment xml:lang="pl">Kod źródłowy Objective-C</comment>
    <comment xml:lang="pt">código fonte Objective-C</comment>
    <comment xml:lang="pt_BR">Código fonte Objective-C</comment>
    <comment xml:lang="ro">Cod sursă Objective-C</comment>
    <comment xml:lang="ru">исходный код Objective-C </comment>
    <comment xml:lang="sk">Zdrojový kód v Objective-C</comment>
    <comment xml:lang="sl">Datoteka izvorne kode Objective-C</comment>
    <comment xml:lang="sq">Kod burues C objekt</comment>
    <comment xml:lang="sr">Објектни-C изворни ко̂д</comment>
    <comment xml:lang="sv">Objective-C-källkod</comment>
    <comment xml:lang="uk">вихідний код мовою Objective-C</comment>
    <comment xml:lang="vi">Mã nguồn Objective-C</comment>
    <comment xml:lang="zh_CN">Objective-C 源代码</comment>
    <comment xml:lang="zh_TW">Objective-C 源代碼</comment>
    <sub-class-of type="text/x-csrc"/>
    <magic priority="30">
      <match value="#import" type="string" offset="0"/>
    </magic>
    <glob pattern="*.m"/>
  </mime-type>
  -->
  <mime-type type="text/x-ocaml">
    <comment>OCaml source code</comment>
...

4. Update the mime-type database:

sudo update-mime-database /usr/share/mime/

5. Restart gedit

Source: Nothing Fancy, Just Blogging.

Leave the first comment

Do It Yourself: Whiteboard (Part 2)

Continued from Part 1.

So the verdict is in.

Acrylic Polycarbonate Polypropylene
Acrylic Polycarbonate Polypropylene Final

(Click for full size)

Polypropylene works the best.

It looks like the ghosting correlated pretty well with the water absorption numbers I listed last post. Also, its interesting to note that the low odor markers cleaned up much better than the bold markers did on the two clear plastics.

5 days later, this is how it cleaned up without any spray or cleaner:

Polypropylene Cleaned

Polypropylene Cleaned

Now, because the polypropylene was translucent, I was concerned that I wouldn’t be able to put a background behind it. However, since the polypropylene was thin enough, it turned out alright:

Polypropylene with Background

Polypropylene With Background

I’ll probably vary the color of the gridlines for the background to see which one works the best, but even 100% wasn’t too bad. In the picture, the squares are 1 inch x 1 inch and the line thickness is 3 points.

Next step will be ordering the full size sheet of the polypropylene (hopefully they’ll be able to ship it with some protective plastic covering), getting a background printed up (probably from Kinkos) and mount it to a backing board (I’m thinking MDF right now, but I might come up with something else.

Stay Tuned.

Update: The project is continued here.

Leave the first comment

Do It Yourself: Whiteboard

In my experience, whiteboards are either cheap or very expensive (heh). By that I mean, they are either of very poor quality or they are very costly. I was looking for a few large whiteboards to place around and I decided to see if it was possible to make your own. It’s always easier just to buy things, but I had a few reasons to want to make my own:
1. Save money.
2. Build a quality product.
3. Interchangeable backgrounds – while looking online for whiteboards, I came across some that had permanent guidelines for things like square gridlines (think graph paper), Gantt charts, even a polar coordinate system. While I think I’d prefer the graph paper-type background most often, the ability to change backgrounds I thought would be huge.
4. For the sake of a new project.

In the past, I’ve messed around with the idea of using plastic sheet for a whiteboard, intending on spray painting the reverse of the plastic sheet white to make the marker colors stand out. The problem I had in my quickly-abandoned project was marker smearing during erasing and ghosting if left on longer than a few hours:

Example of Ghosting

Example of Ghosting (streakmaster.com)

While searching, I came across some whiteboard projects online, such as:
»Showerboard Whiteboard
»Clear Glass Whiteboard

My idea, however, was a bit different than either. Realizing the most important part was the writing surface, and the background could be figured out later, I ordered some sample plastic sheet to determine which (if any) would be suitable for my needs. I would evaluate them on (in roughly this order):
a) Appearance (e.g. How well do the colors stand out? How well do the colors apply to the plastic? Does the sheet come shipped with protective wrap, preventing dings and scratches during cut and transit?)
b) Clean-up (e.g. How well does this material clean up? Do the markers ghost or smear? Are there special products needed to completely remove the ink, if its even possible?)
c) Rigidity (I wanted to go with a thinner plastic sheet for reasons I describe below, but I wanted the plastic to provide some sort of rigity)

For my first candidate materials, I picked Acrylic (trade name: Plexiglas), Polycarbonate (trade name: Lexan), and Polypropylene, all from freckleface.com. In order to test the materials, I ordered the smallest 1/8″ think pieces they sold (12″ x 12″) which came to roughly $18 with shipping.

Upon arrival, I noticed that the acrylic and polycarbonate came with plastic wrap covering, in order to protect the surface from scratches and dings during cutting and handling — the polypropylene did not and arrived with slightly scratched surfaces. The acrylic and polycarbonate were both beautifully clear (despite the photographs which may lead you to believe that the acrylic is tinted blue — that is simply the protective plastic on the back of the pane) and the polypropylene was a nice, consistent, translucent white color.

I numbered each panel and wrote on it with different markers. I used both Expo Low Odor series and Expo Bold series dry erase markers:
Expo Low Odor MarkerExpo Bold Marker

You can see larger versions of each picture by clicking on it.

1.Acrylic 2.Polycarbonate 3.Polypropylene
On White Background
On Dark Background

It was quickly apparent what effect the depth of the plastic sheet had. On the white backed tests, if looked at from a direction not in line with the light source, the markings on the front of the plastic would cast shadows on to the white backing, causing the text or writing to appear blurred.

Sharp (when light source is in-line) Depth Blur Example - Sharp
Blurry (when light source is out-of-line) Depth Blur Example - Blurry

This was quite an annoyance, but it was only really apparent when the light source differed dramatically. Clearly, the thicker the piece of plastic sheet, the more this problem reveals itself — so I’ll be sure to order the thinnest piece of plastic sheet possible.

Along with the photographs above, here’s a rough comparison of these plastic sheet types:

1.Acrylic 2.Polycarbonate 3.Polypropylene
Color Clear Clear Translucent White
Protective Wrap? Yes Yes No
1/8″ x 12″ x 12″ Cost $3.16 $3.90 $3.87
1/8″ x 36″ x 48″ Cost $32.10 $50.80 $33.61
Impact Strength 0.9 13 1.9
Water Absorption 0.03 0.02 0.008

(Material Specifications from here.)

The current plan is to erase a portion of the board every 12 or so hours and document any ghosting / smearing. Then, in the case there is some ghosting or smearing, I’m going to use some of the available cleaners and see how the materials perform. Stay tuned!

UPDATE: continued here.

One comment so far, add another

Linux: Extract audio from .mp4 (video + audio) to .mp3 (audio)

I found myself in a situation where the only access I had to an audio track was having it embedded in an .mp4 file and I wanted just the audio in an .mp3 file. This probably isn’t a common occurrence, but seeing as this could help extract the audio from .mp4s downloaded from popular video sites, this might be of some use.

First of all, I realize there are probably more ways to do this than can be reasonably listed. In fact, there are probably a bunch of tools that do this exact same thing. This was a one off thing, really, so I didn’t spend time looking for a package that did everything. It was only after I did it once that I decided to turn it into a shell script to make it easy to use again in the future.

Note: You need mplayer and lame for this.

[james@fractal ~]$ lame -?
LAME 64bits version 3.98.3 (http://www.mp3dev.org/)
...
[james@fractal ~]$ mplayer -h
...
MPlayer SVN-r31628-4.4.4 (C) 2000-2010 MPlayer Team
#!/bin/bash
#
# EDWARDS RESEARCH
# www.edwards-research.com
#
# This converts the audio from .mp4 files that include video (e.g. youtube.com streams) to
# .mp3 files.
#

# If file exists, set $FILE
#   I know this is a sloppy way to handle command line arguments -- I'm ok with that.  (I
#   was going to provide for options, blah blah...)
if [[ -e ${1} ]] ; then
    FILE=${1}
fi

# Ensure input file exits
if [[ -z $FILE ]] ; then
    echo "File not found -- exiting."
    exit
fi

# Extract Filename
base=$(basename "${FILE}" .mp4)

# Dump audio from .mp4 to .wav with mplayer
#   So, it looks as if it doesn't make a difference in terms of the output (at least from
#   my small test group) whether you pick pcm:waveheader or pcm:fast. pcm:waveheader takes
#   more than twice as long to convert but pcm:fast complains.  I'm going to leave it at
#   waveheader because I'm not in a rush and I'd rather not have the warnings.  Feel free
#   to change this to pcm:fast and experiment.
#       -ao pcm:waveheader -> 59 seconds, 4625553 byte .mp3
#       -ao pcm:fast       -> 22 seconds, 4625553 byte .mp3
#
#   mplayer  -vc null -vo null -nocorrect-pts -ao pcm:fast "${FILE}"
#
mplayer -vc null -vo null -nocorrect-pts -ao pcm:waveheader "${FILE}"
RV=$?
if [[ $RV != 0 ]] ; then
    echo "mplayer completed unsuccessfully -- exiting."
    exit
fi

# Convert .wav to .mp3
lame -h -b 192 audiodump.wav "${base}.mp3" ${VERB}
RV=$?
if [[ $RV != 0 ]] ; then
    echo "lame completed unsuccessfully -- exiting."
    exit
fi

# Cleanup Temporary File
rm audiodump.wav

echo "Conversion complete."
2 comments so far, add yours