2

I have a bunch of LilyPond files in the following format:

\score {
  \new StaffGroup = "" \with {
    instrumentName = \markup { \bold \huge \larger "1." }
  }
  <<
    \new Staff = "celloI" \with { midiInstrument = #"cello" }

    \relative c {
      \clef bass
      \key c \major
      \time 3/4

      \tuplet 3/2 4 {
        c8(\downbow\f b c e g e)
      } c'4                                         | %01
      \tuplet 3/2 4 {c,8( b c e f a) } c4           | %02
      \tuplet 3/2 4 { g,8( d' f g f d) } b'4        | %03
    }
  >>
  \layout {}
  \midi {}
}

How would one extract the \relative c {...} block into a new file, so it would look like this:

\relative c {
  \clef bass
  \key c \major
  \time 3/4

  \tuplet 3/2 4 {
    c8(\downbow\f b c e g e)
  } c'4                                         | %01
  \tuplet 3/2 4 {c,8( b c e f a) } c4           | %02
  \tuplet 3/2 4 { g,8( d' f g f d) } b'4        | %03
}

A fix of the indentation is not necessarily needed in this case. Would that be an awk or csplit task? What would it look like?

Jeff Schaller
  • 66,199
  • 35
  • 114
  • 250
nath
  • 5,430
  • 9
  • 45
  • 87

1 Answers1

2

You might want to think about what to do with the output, like use the input name with an extension for the output. Modifying indentation should not be hard.

This script lists any number of files named on the command line, and any number of such blocks in one file.

This is the output from your sample:

Paul--) ./LilyPond Lily.txt
    \relative c {
      \clef bass
      \key c \major
      \time 3/4

      \tuplet 3/2 4 {
        c8(\downbow\f b c e g e)
      } c'4                                         | %01
      \tuplet 3/2 4 {c,8( b c e f a) } c4           | %02
      \tuplet 3/2 4 { g,8( d' f g f d) } b'4        | %03
    }

This is the script.

Paul--) cat LilyPond
#! /bin/bash

function Relative {

    local AWK='''
BEGIN { reRel = "[\\\\]relative[ ]+c"; }
function cntBrace (tx, Local, n) {
    n += gsub ("{", "{", tx);
    n -= gsub ("}", "}", tx);
    return (n);
}
$0 ~ reRel || nBrace > 0 { print; nBrace += cntBrace( $0); }
'''
    awk -f <( echo "${AWK}" ) "${@}"
}

#### Script Body Starts Here.

    Relative "${@:--}"

Paul_Pedant
  • 8,228
  • 2
  • 18
  • 26
  • 1
    Awesome, THX; I used: a for loop like `for file in *.ily; do Relative "$file" > "${file}~"; mv "${file}~" "$file"; done`. – nath Dec 01 '19 at 22:36
  • I'm not that brave -- I hope you have a backup for the files. Block structured languages don't rely too much on line layout, so this can fail in bad ways: (a) If the "relative" line does not include the first {, it misses the block; (b) The whole start line will be output, not clipped up to "relative"; (c) The whole end line will be output, not clipped to the exact "}". If your files are generated by code, they should be OK, but manual edits may not conform. – Paul_Pedant Dec 02 '19 at 10:34
  • 1
    Fourth (blindingly obvious) failure mode. If your file does not have a "relative" block at all, the whole output file is empty. Quick fix would be to wc -l your temporary file, and if less than minimum possible valid block (maybe 5 lines), flag a warning and skip the mv command. – Paul_Pedant Dec 02 '19 at 10:48