Shell vs “Shell”

September 23, 2009 at 5:29 am 4 comments

| Gabriel |

Two thoughts on Stata’s “shell” command (which let’s Stata access the OS command line).

First, I just discovered the “ashell” command (“ssc install ashell”), which pipes the shell’s “standard out” to Stata return macros. For short output this can be a lot more convenient than what I had been doing, which was to pipe stdout to a file, then use “file” or “insheet” to get that into Stata. For instance, my “do it to everything in a directory” script is a lot simpler if I rewrite it to use “ashell” instead of “shell”.

ashell ls *.dta
* note that ashell tokenizes stdout so to use it as one string you need to reconstruct it
forvalues stringno=1/`r(no)' {
  local stdout "`stdout' `r(o`stringno')'"
}
*because i used "ls", stdout is now a list of files, suitable for looping
*as an example, i'll load each file and export it to excel 2007 format
foreach file in `stdout' {
  use `file', clear
  xmlsave `file'.xlsx, doctype(excel) replace
}

The second thing is a minor frustrations I’ve had with the Stata “shell” command. Unlike a true shell in the terminal, it has no concept of a “session” but treats each shell command as coming ex nihilio. A related problem is it doesn’t read any of your preference files (e.g., ~/.bashrc). Since shell preference files are just shell scripts read automatically at the beginning of a session, the latter is a logical corollary of the first. Ignoring the preference files is arguably a feature, not a bug, as it forces you to write do-files that will travel between machines (at least if both are Windows or both are POSIX).

Anyway, here’s a simple example of what I mean. Compare running this (working) bash script in a terminal session:

alias helloworld="echo 'hello world'"
helloworld

with the (failing) equivalent through Stata’s shell command:

shell alias helloworld="echo 'hello world'"
shell helloworld

Anyway, I think the best work-around is to use multiple script files that either invoke each other as a daisy chain or are all invoked by a master shell script. So, say you needed Stata to do some stuff, then process the data with some Unix tools, then do some more stuff with Stata, then typeset the output as PDF. One way to do this would be to have a shell script that says to use the first do file, then the perl script, then the second do-file, then a latex command. Alternately you could make the last line of the first do-file a “shell” command to invoke the perl script, the last line of the perl script a “system” or “exec” command to invoke the second do-file, and the last line of the second do-file is a “shell” command to invoke ghostscript or lyx.

Also note that if you’re doing the master shell script approach you can do some interesting stuff with “make” so as to ensure that the dependencies in a complicated workflow execute in the right order. See here and here for more info.

Finally, if you just want to read a long path that you usually “alias”, the simplest thing is to just copy the full path from bashrc, you can do this directly from Stata by typing “view ~/.bashrc”

Entry filed under: Uncategorized. Tags: , .

So long (distinctive) Notre Dame economics Stata console mode

4 Comments

  • 1. Michael P. Manti  |  September 23, 2009 at 1:50 pm

    In your example, why do you call out to the OS shell? Is there a reason to prefer doing that to relying on native Stata?

    The following snippet is untested, but something like it should work (except perhaps in the perverse case where the string “.dta” occurs more than once in the filename).

    local dtas : dir . files “*.dta”, respectcase

    foreach file in `files’ {
    local pos = strpos(“`dta'”, “.dta”)
    local xlsx = substr(“`dta'”, 1, `pos’-1) + “.xslsx”
    use “`dta'”, clear
    xmlsave “`xlsx'”, doctype(excel) replace
    }

    • 2. gabrielrossman  |  September 23, 2009 at 2:10 pm

      your way is indeed preferable. i assumed that there was no way to capture the output of a native “dir” command because it’s not documented as a return macro under “dir” in the manual. (on checking i see that your way is documented under “extended_fcn” but it’s a shame it’s not cross-referenced under “dir”).

      i think there’s a slight error in your script though, the “foreach” command should refer to the global $_dtas instead of the local `files’. or at least, that’s how i got it to work when i tested it.

      thanks very much for teaching me this parsimonious bit of code.

  • 3. Michael P. Manti  |  September 23, 2009 at 3:24 pm

    You’re welcome. And thank you for the handy tips I’ve learned from your blog.

    Corrected (and tested!) code snippet:

    local dtas : dir . files “*.dta”, respectcase
    foreach dta of local dtas {
    local pos = strpos(“`dta'”, “.dta”)
    local xlsx = substr(“`dta'”, 1, `pos’-1) + “.xslsx”
    use “`dta'”, clear
    xmlsave “`xlsx'”, doctype(excel) replace
    }

  • 4. Max Masnick  |  September 29, 2011 at 11:36 am

    If you want to load in your .bash_profile, .bashrc, .zshrc, etc., the following works for me:

    shell source /Users/me/.zshrc && /path/to/something.sh


The Culture Geeks


%d bloggers like this: