Example 03: Text Manipulation and Comparison

Regexes in the Shell and elsewhere

Grep and its many derivatives and relations search for text inside files. The power of grep-like tools comes from their use of regular expressions.

# Search recursively through all subdirs below files/ in the project folder
grep -r "Kieran" files
files/examples/ulysses.txt:Bride and S. Kieran and S. Canice of Kilkenny and S. Jarlath of Tuam
files/examples/_make-example/mypaper.md:author: Kieran Healy
files/bib/references.bib:  Author   = {Kieran Healy},
files/bib/references.bib:  Author   = {Kieran Healy},
files/bib/mptc_references.bib:  author = {Healy, Kieran},
files/bib/mptc_references.bib:  author = {Healy, Kieran},

We can use wildcards or “globs”:

# Search for a string in all qmd files in the current directory
grep "format: " *.qmd
README.qmd:format: gfm

Like most shell commands grep and friends have switches that change their behavior:

# Count the number of matches inside each file
grep -c "Unix" slides/*.qmd
slides/00-slides.qmd:0
slides/01a-slides.qmd:0
slides/01b-slides.qmd:1
slides/02-slides.qmd:32
slides/03-slides.qmd:0
slides/04-slides.qmd:0
slides/05-slides.qmd:0
slides/05b-slides.qmd:0
slides/06-slides.qmd:0
slides/07-slides.qmd:0
slides/08-slides.qmd:0
slides/08a-slides.qmd:0

We can use more advanced regular expressions too:

# -E for "extended regular expression" form
grep -E "^The sky|night.$" files/examples/sentences.txt
The sky that morning was clear and bright blue.
The club rented the rink for the fifth night.
The clan gathered on each dull night.
The sky in the west is tinged with orange red.
The mule trod the treadmill day and night.
The corner store was robbed last night.

Another example:

grep -E "^A c|red.$" files/examples/sentences.txt
A cup of sugar makes sweet fudge.
A cramp is no small danger on a swim.
Live wires should be kept covered.
A cloud of dust stung his tender eyes.
A castle built from sand fails to endure.
A child's wit saved the day for us.
Mark the spot with a sign painted red.
A cone costs five cents on Mondays.
A chink in the wall allowed a draft to blow.
A cold dip restores health and zest.
A clean neck means a neat collar.
The green light in the brown box flickered.
He put his last cartridge into the gun and fired.
A cruise in warm waters in a sleek yacht is fun.
The sky in the west is tinged with orange red.

ripgrep

Ripgrep, or rg is quicker than grep and has some nice features, such as ignoring things in your .gitignore file by default. However, it won’t be installed by default on your computer.

rg Kieran slides
slides/03-slides.qmd:  - name: Kieran Healy
slides/08a-slides.qmd:  - name: Kieran Healy
slides/01a-slides.qmd:  - name: Kieran Healy
slides/04-slides.qmd:  - name: Kieran Healy
slides/00-slides.qmd:  - name: Kieran Healy
slides/06-slides.qmd:  - name: Kieran Healy
slides/06-slides.qmd:Author: Kieran Healy <kjhealy@gmail.com>
slides/06-slides.qmd:Author: Kieran Healy <kjhealy@gmail.com>
slides/06-slides.qmd:Author: Kieran Healy <kjhealy@gmail.com>
slides/06-slides.qmd:Author: Kieran Healy <kjhealy@gmail.com>
slides/06-slides.qmd:Author: Kieran Healy <kjhealy@gmail.com>
slides/06-slides.qmd:Author: Kieran Healy <kjhealy@gmail.com>
slides/06-slides.qmd:Author: Kieran Healy <kjhealy@gmail.com>
slides/06-slides.qmd:Author: Kieran Healy <kjhealy@gmail.com>
slides/06-slides.qmd:Author: Kieran Healy <kjhealy@gmail.com>
slides/06-slides.qmd:Author: Kieran Healy <kjhealy@gmail.com>
slides/06-slides.qmd:Author: Kieran Healy <kjhealy@gmail.com>
slides/06-slides.qmd:Author: Kieran Healy <kjhealy@gmail.com>
slides/06-slides.qmd:Author: Kieran Healy <kjhealy@gmail.com>
slides/07-slides.qmd:  - name: Kieran Healy
slides/01b-slides.qmd:  - name: Kieran Healy
slides/05b-slides.qmd:  - name: Kieran Healy
slides/05-slides.qmd:  - name: Kieran Healy
slides/08-slides.qmd:  - name: Kieran Healy
slides/02-slides.qmd:  - name: Kieran Healy


# You can sepecify /types/ of files to look at with -t
rg -t yaml "url:" .
./_variables.yml:  url: "https://www.kieranhealy.org"
./_variables.yml:  url: "https://mptc.io"
./_variables.yml:  url: "https://www.duke.edu"
./_variables.yml:  url: "https://sociology.duke.edu/"
./_quarto.yml:  site-url: "https://mptc.io"
./_quarto.yml:  repo-url: "https://github.com/kjhealy/mptc"

Standard shell tools like sed, awk, and grep can all use some version of regular expressions, as can all good text editors (including the text editor built into RStudio).

Perl

Perl one-liners require care

When experimenting with scripts and commands like this, the files and folders you are working on should generally be under version control so you can roll back to the previous version if you make a serious error.

There’s also Perl, a programming language that’s been displaced to some extent by Python but which remains very good at compactly manipulating strings. It can easily replace a lot of shell scripting involving grep, sed, or awk with a line or two of code.

One useful (but be-careful-not-to-cut-yourself dangerous) thing Perl can do is easily edit a lot of files in place. For example, imagine you have a project with a bunch of .Rmd files that have code in them for graphs. And imagine further that many of these files have bits of code that uses the percent_format function from the {scales} package. Then one day the {scales} maintainer announces that percent_format is now deprecated and you should use label_percent instead. How can you change all instances of one to the other across all .Rmd files in your project folder (or a series of project folders)?

# Find every quarto file beneath the current directory
# Then edit each one in place to replace every instance of 
# `percent_format` with `label_percent`
find . -name "*.qmd" | xargs perl -p -i -e "s/percent_format/label_percent/g"

You can protect a bit against the dangers of doing this by making the -i option create backup files of everything it touches:

# Find every quarto file beneath the current directory
# Then edit each one in place to replace every instance of 
# `percent_format` with `label_percent`
find . -name "*.qmd" | xargs perl -p -i.orig -e "s/percent_format/label_percent/g"

Here the -i.orig flag will back up e.g. analysis.Rmd to analysis.Rmd.orig. Note that you don’t put a space between the -i part and the .orig backup extension. (You can choose any extension you like.)

For more on Perl oneliners see, for example, the Perl one-liners cookbook

Diffing

Comparing the differences between two files is called diffing them, and diff is the command that does it for you. This is best done inside a text editor or dedicated diffing application, because usually it arises when we want to interactively choose one or other version to accept or reject. But you can still do a fair amount just at the console. For example here we compare the two versions of Tennyson’s “The Lady of Shalott”, the one he wrote in 1832 versus the one he revised ten years later. (The output here is messier than it would appear at the console, and not highlighted properly.)

diff --side-by-side files/examples/shalott_1832.txt files/examples/shalott_1842.txt 

Part I                                                          Part I

On either side the river lie                                    On either side the river lie
Long fields of barley and of rye,                               Long fields of barley and of rye,
That clothe the wold and meet the sky;                          That clothe the wold and meet the sky;
And thro' the field the road runs by                            And thro' the field the road runs by
       To many-tower'd Camelot;                                        To many-tower'd Camelot;
The yellow-leaved waterlily                                   | And up and down the people go,
The green-sheathed daffodilly                                 | Gazing where the lilies blow
Tremble in the water chilly                                   | Round an island there below,
       Round about Shalott.                                   |        The island of Shalott.
                                                              |
Willows whiten, aspens shiver.                                | Willows whiten, aspens quiver,
The sunbeam showers break and quiver                          | Little breezes dusk and shiver
In the stream that runneth ever                               | Thro' the wave that runs for ever
By the island in the river                                      By the island in the river
       Flowing down to Camelot.                                        Flowing down to Camelot.
Four gray walls, and four gray towers                         | Four gray walls, and four gray towers,
Overlook a space of flowers,                                    Overlook a space of flowers,
And the silent isle imbowers                                    And the silent isle imbowers
       The Lady of Shalott.                                            The Lady of Shalott.

Underneath the bearded barley,                                | By the margin, willow veil'd,
The reaper, reaping late and early,                           | Slide the heavy barges trail'd
Hears her ever chanting cheerly,                              | By slow horses; and unhail'd
Like an angel, singing clearly,                               | The shallop flitteth silken-sail'd
       O'er the stream of Camelot.                            |        Skimming down to Camelot:
Piling the sheaves in furrows airy,                           | But who hath seen her wave her hand?
Beneath the moon, the reaper weary                            | Or at the casement seen her stand?
Listening whispers, ' 'Tis the fairy,                         | Or is she known in all the land,
       Lady of Shalott.'                                      |        The Lady of Shalott?
                                                              |
The little isle is all inrail'd                               | Only reapers, reaping early
With a rose-fence, and overtrail'd                            | In among the bearded barley,
With roses: by the marge unhail'd                             | Hear a song that echoes cheerly
The shallop flitteth silken sail'd,                           | From the river winding clearly,
       Skimming down to Camelot.                              |        Down to tower'd Camelot:
A pearl garland winds her head:                               | And by the moon the reaper weary,
She leaneth on a velvet bed,                                  | Piling sheaves in uplands airy,
Full royally apparelled,                                      | Listening, whispers " 'Tis the fairy
       The Lady of Shalott.                                   |        Lady of Shalott."

Part II                                                         Part II

No time hath she to sport and play:                           | There she weaves by night and day
A charmed web she weaves alway.                               | A magic web with colours gay.
A curse is on her, if she stay                                | She has heard a whisper say,
Her weaving, either night or day,                             | A curse is on her if she stay
       To look down to Camelot.                                        To look down to Camelot.
She knows not what the curse may be;                          | She knows not what the curse may be,
Therefore she weaveth steadily,                               | And so she weaveth steadily,
Therefore no other care hath she,                             | And little other care hath she,
       The Lady of Shalott.                                            The Lady of Shalott.

She lives with little joy or fear.                            | And moving thro' a mirror clear
Over the water, running near,                                 | That hangs before her all the year,
The sheepbell tinkles in her ear.                             | Shadows of the world appear.
Before her hangs a mirror clear,                              | There she sees the highway near
       Reflecting tower'd Camelot.                            |        Winding down to Camelot:
And as the mazy web she whirls,                               | There the river eddy whirls,
She sees the surly village churls,                            | And there the surly village-churls,
And the red cloaks of market girls                            | And the red cloaks of market girls,
       Pass onward from Shalott.                                       Pass onward from Shalott.

Sometimes a troop of damsels glad,                              Sometimes a troop of damsels glad,
An abbot on an ambling pad,                                     An abbot on an ambling pad,
Sometimes a curly shepherd lad,                               | Sometimes a curly shepherd-lad,
Or long-hair'd page in crimson clad,                            Or long-hair'd page in crimson clad,
       Goes by to tower'd Camelot:                            |        Goes by to tower'd Camelot;
And sometimes thro' the mirror blue                             And sometimes thro' the mirror blue
The knights come riding two and two:                            The knights come riding two and two:
She hath no loyal knight and true,                              She hath no loyal knight and true,
       The Lady of Shalott.                                            The Lady of Shalott.

But in her web she still delights                               But in her web she still delights
To weave the mirror's magic sights,                             To weave the mirror's magic sights,
For often thro' the silent nights                               For often thro' the silent nights
A funeral, with plumes and lights                               A funeral, with plumes and lights
       And music, came from Camelot:                          |        And music, went to Camelot:
Or when the moon was overhead                                 | Or when the moon was overhead,
Came two young lovers lately wed;                             | Came two young lovers lately wed:
'I am half sick of shadows,' said                             | "I am half sick of shadows," said
       The Lady of Shalott.                                            The Lady of Shalott.

Part III                                                        Part III

A bow-shot from her bower-eaves,                                A bow-shot from her bower-eaves,
He rode between the barley-sheaves,                             He rode between the barley-sheaves,
The sun came dazzling thro' the leaves,                         The sun came dazzling thro' the leaves,
And flam'd upon the brazen greaves                            | And flamed upon the brazen greaves
       Of bold Sir Lancelot.                                           Of bold Sir Lancelot.
A red-cross knight for ever kneel'd                             A red-cross knight for ever kneel'd
To a lady in his shield,                                        To a lady in his shield,
That sparkled on the yellow field,                              That sparkled on the yellow field,
       Beside remote Shalott.                                          Beside remote Shalott.

The gemmy bridle glitter'd free,                                The gemmy bridle glitter'd free,
Like to some branch of stars we see                             Like to some branch of stars we see
Hung in the golden Galaxy.                                      Hung in the golden Galaxy.
The bridle bells rang merrily                                   The bridle bells rang merrily
       As he rode down from Camelot:                          |        As he rode down to Camelot:
And from his blazon'd baldric slung                             And from his blazon'd baldric slung
A mighty silver bugle hung,                                     A mighty silver bugle hung,
And as he rode his armour rung,                                 And as he rode his armour rung,
       Beside remote Shalott.                                          Beside remote Shalott.

All in the blue unclouded weather                               All in the blue unclouded weather
Thick-jewell'd shone the saddle-leather,                        Thick-jewell'd shone the saddle-leather,
The helmet and the helmet-feather                               The helmet and the helmet-feather
Burn'd like one burning flame together,                         Burn'd like one burning flame together,
       As he rode down from Camelot.                          |        As he rode down to Camelot.
As often thro' the purple night,                                As often thro' the purple night,
Below the starry clusters bright,                               Below the starry clusters bright,
Some bearded meteor, trailing light,                            Some bearded meteor, trailing light,
       Moves over green Shalott.                              |        Moves over still Shalott.

His broad clear brow in sunlight glow'd;                        His broad clear brow in sunlight glow'd;
On burnish'd hooves his war-horse trode;                        On burnish'd hooves his war-horse trode;
From underneath his helmet flow'd                               From underneath his helmet flow'd
His coal-black curls as on he rode,                             His coal-black curls as on he rode,
       As he rode down from Camelot.                          |        As he rode down to Camelot.
From the bank and from the river                                From the bank and from the river
He flash'd into the crystal mirror,                             He flash'd into the crystal mirror,
'Tirra lirra, tirra lirra:'                                   | "Tirra lirra," by the river
       Sang Sir Lancelot.                                              Sang Sir Lancelot.

She left the web, she left the loom                           | She left the web, she left the loom,
She made three paces thro' the room                           | She made three paces thro' the room,
She saw the water-flower bloom,                               | She saw the water-lily bloom,
She saw the helmet and the plume,                               She saw the helmet and the plume,
       She look'd down to Camelot.                                     She look'd down to Camelot.
Out flew the web and floated wide;                              Out flew the web and floated wide;
The mirror crack'd from side to side;                           The mirror crack'd from side to side;
'The curse is come upon me,' cried                            | "The curse is come upon me," cried
       The Lady of Shalott.                                            The Lady of Shalott.

Part IV                                                         Part IV

In the stormy east-wind straining,                              In the stormy east-wind straining,
The pale yellow woods were waning,                              The pale yellow woods were waning,
The broad stream in his banks complaining,                      The broad stream in his banks complaining,
Heavily the low sky raining                                     Heavily the low sky raining
       Over tower'd Camelot;                                           Over tower'd Camelot;
Outside the isle a shallow boat                               | Down she came and found a boat
Beneath a willow lay afloat,                                  | Beneath a willow left afloat,
Below the carven stern she wrote,                             | And round about the prow she wrote
       The Lady of Shalott.                                   |        The Lady of Shalott.
                                                              |
A cloudwhite crown of pearl she dight,                        | And down the river's dim expanse
All raimented in snowy white                                  | Like some bold seër in a trance,
That loosely flew (her zone in sight                          | Seeing all his own mischance—
Clasp'd with one blinding diamond bright)                     | With a glassy countenance
       Her wide eyes fix'd on Camelot,                        |        Did she look to Camelot.
Though the squally east-wind keenly                           | And at the closing of the day
Blew, with folded arms serenely                               | She loosed the chain, and down she lay;
By the water stood the queenly                                <
       Lady of Shalott.                                       <
                                                              <
With a steady stony glance—                                   <
Like some bold seer in a trance,                              <
Beholding all his own mischance,                              <
Mute, with a glassy countenance—                              <
       She look'd down to Camelot.                            <
It was the closing of the day:                                <
She loos'd the chain, and down she lay;                       <
The broad stream bore her far away,                             The broad stream bore her far away,
       The Lady of Shalott.                                            The Lady of Shalott.

As when to sailors while they roam,                           | Lying, robed in snowy white
By creeks and outfalls far from home,                         | That loosely flew to left and right—
Rising and dropping with the foam,                            | The leaves upon her falling light—
From dying swans wild warblings come,                         | Thro' the noises of the night
       Blown shoreward; so to Camelot                         |        She floated down to Camelot:
Still as the boathead wound along                             | And as the boat-head wound along
The willowy hills and fields among,                             The willowy hills and fields among,
They heard her chanting her deathsong,                        | They heard her singing her last song,
       The Lady of Shalott.                                            The Lady of Shalott.

A longdrawn carol, mournful, holy,                            | Heard a carol, mournful, holy,
She chanted loudly, chanted lowly,                            | Chanted loudly, chanted lowly,
Till her eyes were darken'd wholly,                           | Till her blood was frozen slowly,
And her smooth face sharpen'd slowly,                         | And her eyes were darken'd wholly,
       Turn'd to tower'd Camelot:                             |        Turn'd to tower'd Camelot.
For ere she reach'd upon the tide                               For ere she reach'd upon the tide
The first house by the water-side,                              The first house by the water-side,
Singing in her song she died,                                   Singing in her song she died,
       The Lady of Shalott.                                            The Lady of Shalott.

Under tower and balcony,                                        Under tower and balcony,
By garden wall and gallery,                                   | By garden-wall and gallery,
A pale, pale corpse she floated by,                           | A gleaming shape she floated by,
Deadcold, between the houses high,                            | Dead-pale between the houses high,
       Dead into tower'd Camelot.                             |        Silent into Camelot.
                                                              > Out upon the wharfs they came,
Knight and burgher, lord and dame,                              Knight and burgher, lord and dame,
To the planked wharfage came:                                 | And round the prow they read her name,
Below the stern they read her name,                           <
       The Lady of Shalott.                                            The Lady of Shalott.

They cross'd themselves, their stars they blest,              | Who is this? and what is here?
Knight, minstrel, abbot, squire, and guest.                   | And in the lighted palace near
There lay a parchment on her breast,                          | Died the sound of royal cheer;
That puzzled more than all the rest,                          | And they cross'd themselves for fear,
       The wellfed wits at Camelot.                           |        All the knights at Camelot:
'The web was woven curiously,                                 | But Lancelot mused a little space;
The charm is broken utterly,                                  | He said, "She has a lovely face;
Draw near and fear not,—this is I,                            | God in his mercy lend her grace,
       The Lady of Shalott.'                                  |        The Lady of Shalott."