...XSH - XML Editing Shell

Support This Project

SF project page
Hosted on SourceForge
RSS feed

XSH - simple usage examples

A lot of small examples can be found in the documentation.

Some practical examples can also be found in the XSH category of Chris Burbridge's blog.

The 276 lines of XSH code used to generate these pages

Here you may find the XSH code I use to create XSH website from simple XML/HTML documents and templates.

Quick imaginary tutorial to XSH

  1. Start xsh shell in the interactive mode with xsh -i

  2. Open an XML document (in this example represented by an imaginary mydoc.xml document with containing various information about J.R.R.Toliken's Middle-Earth). The document contains chapters, each having a title. We list the titles.

    $scratch/>$f := open mydoc.xml
    $f/>list //chapter/title
       <title>The Music of the Ainur</title>
       <title>Of the Valar</title>
       <title>Of the Maiar</title>
  3. First, we try to add an identifier to the first title and list the results.

    $f/>add attribute "id='t1'" into //chapter/title
    $f/>list //chapter/title
       <title id="t1">Foreword</title>
       <title>The Music of the Ainur</title>
       <title>Of the Valar</title>
       <title>Of the Maiar</title>
  4. Ok, what about adding a unique identifier of the form id='tN' (where N is a number) to every title? There are many ways to do that, pick any you like (recall, that in XPath, dot . matches the context node (same as self::node()))

    $f/> $i=1; foreach //chapter/title set @id {"t".$i++};
    $f/> foreach //chapter/title set @id concat("t",position());
    $f/>$i=1; foreach //chapter/title { copy xsh:new-attribute('id',concat('t',$i)) into .; $i+=1 }
    $f/>$i=1; foreach //chapter/title { add attribute 'id="t${$i}"' into .; $i+=1 }
    $f/>$i=1; foreach //chapter/title { add attribute concat('id="t',$i,'"') into .; $i+=1 }
    $f/>$i=1; foreach //chapter/title { add attribute xsh:sprintf('id="t%d"',$i) into .; $i+=1 }
    $f/>$i=1; foreach //chapter/title add attribute { 'id="t'.($i++).'"' } into .
    $f/>$i=1; foreach //chapter/title add attribute { sprintf('id="t%d"',$i++) } into .
    $f/>foreach //chapter/title copy xsh:new-attribute('id',concat('t',position())) into .

    All of the above will produce:

    $f/>list //chapter/title
       <title id="t1">Foreword</title>
       <title id="t2">The Music of the Ainur</title>
       <title id="t3">Of the Valar</title>
       <title id="t4">Of the Maiar</title>
  5. Ok, now let's try something a little more complicated. We still want all chapter with titles to have identifers (so now it's not the title that gets an ID but the chapter itself) and we also want the chapters to be numbered (say, we're too lasy to implement this in a renderer). We'll do this in just one step. We first undo our previous identifier addition to the titles by deleting all id attributes from chapter titles. (Note, that quoting the end-of-line with \ only applies when we work with XSH shell interactivelly).

    xsh $f/>remove //chapter/title/@id
    $f/>ls //chapter/title
       <title>What is XML</title>
       <title>How to author XML documents</title>
    $f/> $f/>foreach //chapter/title { \
    > my $pos = position(); \
    > add text "Chapter ${pos}. " prepend title;
    > set ../@id concat('chap',$pos));\
    > }
    $f/>list //chapter/title
       <title>Chapter 1. Foreword</title>
       <title>Chapter 2. The Music of the Ainur</title>
       <title>Chapter 3. Of the Valar</title>
       <title>Chapter 4. Of the Maiar</title>

    List command has (among other options) an option named --depth, which specifies a number of levels of nested subelements to list; three dots (...) in the output indicate skipped content.

    $f/>list //chapter[title] 0
       <chapter id="chap0">...</chapter>
       <chapter id="chap1">...</chapter>
       <chapter id="chap2">...</chapter>
       <chapter id="chap3">...</chapter>
  6. Almost done, but let's make all titles uppercase. There is a perl function uc which does the conversion, so we can simply write:

    $f/>map { uc } //chapter/title/text()
    $f/>list //chapter/title
       <title>CHAPTER 1. FOREWORD</title>
       <title>CHAPTER 2. THE MUSIC OF THE AINUR</title>
       <title>CHAPTER 3. OF THE VALAR</title>
       <title>CHAPTER 4. OF THE MAIAR</title>
  7. Now, let's transform the document using a XSLT stylesheet and obtain a new document in a variable $g:

    $f/> $g := xslt "stylesheets/my_stylesheet.xsl" $f

    You may optionally pass some parameters to the stylesheet. You may also use URL instead of a filename, if you like. There is a lot more (see xslt command description).

    $f/> $docbook_xsl_url = "http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl";
    $f/> $g := xslt $docbook_xsl_url $f fontsize="14" fontfam="times new roman"

    (Note that XSH does not automatically select the result of XSLT transformation as a current document.)

  8. Check the result:

    $f/> list $g
  9. Go to the header of the new document:

    $f/> cd $g/html/head
  10. Sign it:

    $g/html/head> insert chunk "<meta content='Merriadoc Brandybuck' name='author'/>" into .
    $g/html/head> ls --indent .
      <meta name="generator" content="DocBook XSL Stylesheets V1.64.1"/>
      <meta content="Merriadoc Brandybuck" name="author"/>
  11. Now save it as HTML to index.html:

    $g/html/head> save --format html --file "index.html" $g
  12. Save it as XML (just for sure):

    $g/html/head> save --file "index.xhtml" $g
  13. Send it to a friend (via e-mail, say, as HTML - ok ok, I know it should be an attachment, but this is just an example):

    $g/html/head> save --format html --pipe "mail -s 'Hey, you must see this one, Pipin!' peregrin.took@middleearth.ea"