0

sed via Fedora RPM (dnf)

I need to advance the tracks in a cue file in order to add a track for a pre-gap. (TRACK 00 no longer works with shnsplit.) When a script did not work, I used the command line.

sed -i 's/TRACK 01/TRACK 02/' *cue

Does exactly what I want. Track 01 is now track 02. However, I now have two track 02 in the file.

sed -i 's/TRACK 02/TRACK 03/2' *cue

Does absolutely nothing. I am left with two "TRACK 02 AUDIO" listings.

enter image description here

Cyrus
  • 12,309
  • 4
    Do not post images. Use the code blocks to post the actual text. Also, add the expected output to the question. An answer can't be given until that is available. – Nasir Riley Jul 16 '23 at 20:12
  • Please clarify your specific problem or provide additional details to highlight exactly what you need. As it's currently written, it's hard to tell exactly what you're asking. – Community Jul 16 '23 at 21:00
  • 3
    In s/pattern/replacement/n the n refers to the nth occurrence of pattern within the current pattern space. By default, sed processes line-by-line so it's not going to do what you want here unless (for example) you slurp the whole file into pattern space using GNU sed's -z option. – steeldriver Jul 16 '23 at 21:57
  • Don't start with the first one, start with the last. Change /49/50/, then /48/49/, ... , /02/03/, /01/02/. – waltinator Jul 16 '23 at 22:06
  • 1
    @steeldriver you're rewarding someone who posts pictures of text :-( – Chris Davies Jul 16 '23 at 23:08
  • 3
    Please don't post images of text. Copy and paste the text itself into your question and format it as code by selecting it and pressing Ctrl-K or by using the editor's {} icon, or by adding a line containing three backticks before and after the text. – cas Jul 17 '23 at 05:33
  • Does it have to be sed? – jubilatious1 Jul 17 '23 at 09:41
  • 1
    [edit] your question to include concise, testable sample input and expected output, otherwise we're all just guessing at how to help you and have nothing we can copy/paste to test a potential solution against. – Ed Morton Jul 17 '23 at 16:42

2 Answers2

0

Using Raku (formerly known as Perl_6)

Raku is a programming language in the Perl-family of programming languages. I'm posting this answer to highlight Raku's easy-to-read :nth() Regex modifier, which can also be written :1st, :2nd, :3rd, :4th, etc.

Using S/// operator:

~$ raku -e 'given slurp() { put S:nth(6)/ line /|*-> line <-*|/ };'  file

#OR

~$ raku -e 'given slurp() { put S:6th/ line /|-> line <-|/ };' file

Using subst() operator:

~$ raku -e 'put slurp.subst: :nth(6), /line/, "|*-> line <-*|";'  file

#OR

~$ raku -e 'put slurp.subst: :6th, /line/, "|-> line <-|";' file

Sample Input:

1st line Doc
2nd line Sneezy
3rd line Grumpy
4th line Happy
5th line Bashful
6th line Sleepy
7th line Dopey

Sample Output (all code examples):

1st line Doc
2nd line Sneezy
3rd line Grumpy
4th line Happy
5th line Bashful
6th |*-> line <-*| Sleepy
7th line Dopey

https://docs.raku.org/language/regexes#Positional_adverbs
https://docs.raku.org/routine/subst
https://raku.org

jubilatious1
  • 3,195
  • 8
  • 17
0

Let us first start by analyzing what went wrong before trying a solution. Here is your command:

sed -i 's/TRACK 02/TRACK 03/2' *cue

I wouldn't use the -i option of sed, but that is a secondary issue. sed works on lines, not on files with text and a regular expression like "TRACK 02" describes the content of a line, not a file. What your command says is:

If a line contains the string "TRACK 02" twice (or more often), then change the second occurrence of it to "TRACK 03". Like this:

TRACK 02 TRACK 02 TRACK 02 TRACK 02 [...] ==>
TRACK 02 TRACK 03 TRACK 02 TRACK 02 [...]

Also notice, that regular expressions (or, rather, the underlying regular grammars are context-free by definition. Therefore there are some limitations on what you can do. For more information this is a starter, if you are interested I suggest the "Dragon book" ("Compilers: Principles, Techniques, and Tools" by Aho, Sethi and Ullman) for more in-depth analysis.

Another thing you need to understand is how sed works: It reads in the first line of the text to change, then applies the first command, on the result of this it applies the second command and so on, until the last command is reached. If you do not use the -n option then the result is printed, the next line is read in, to which the first command is applied, etc., until the last command is applied to the result of the penultimate command on the last line - upon which sed stops.

This is why you can't do this:

s/TRACK 00/TRACK 01/
s/TRACK 01/TRACK 02/
s/TRACK 02/TRACK 03/
s/TRACK 03/TRACK 04/

This would change "00" to "01", the next command would change "01" to "02", the next "02" to "03" and so on. In the end all lines would read "TRACK 04" (or however long you want to make the list).

What you can do, however, is to do the change in reverse (if you have more than 10 titles add similar lines at the beginning). While we are at it we can also add the pre-gap TRACK 01 automatically:

sed 's/TRACK 09/TRACK 10/
     s/TRACK 08/TRACK 09/
     s/TRACK 07/TRACK 08/
     s/TRACK 06/TRACK 07/
     s/TRACK 05/TRACK 06/
     s/TRACK 04/TRACK 05/
     s/TRACK 03/TRACK 04/
     s/TRACK 02/TRACK 03/
     s/TRACK 01/TRACK 02/
     /FILE/ { a\
TRACK 01
a\
addtional optional text under TRACK 01
        }' /path/to/file

Finally, here is a suggestion how to employ this sed-Script for mass-changings. We put the changed files in some other directory leaving the originals alone. If (only if) we have checked the results and are pleased with them we copy them back overwriting the originals:

for fWork in "*cue" ; do
     sed 'the script from above' "$fWork" > "/other/path/$fWork"
done

Now, go check the results. If it is to your liking:

mv /other/path/*cur /original/path
bakunin
  • 531