-1

I am trying to prepend text to the begging/header of a set of .vtt text files with sed in a loop. I'm not replacing any text, only adding text.

I need some line breaks in the text.

The text should read:

WEBVTT
Kind: captions
Language: eng
File Creation Date: 2023-08

Here is what I am trying:

for file in *.vtt ; do 
sed -i '' 'WEBVTT/nKind: captions/nLanguage: eng/nFile Creation Date: 2023-08' "$file"
done

give me the error: sed: 1: 'WEBVTT/nKind: captions/ ...': invalid command code W

I'm not sure why. I tried switching double/single quotes, but that didn't work.

Kusalananda
  • 333,661
Bleakley
  • 183
  • 4
    You might want to tell us what exactly you're trying to do. What you're trying to change. Your sed instructions are not valid, but in order to help you we need to understand your intentions. Also, you might want to start without a loop to make it simpler. – aviro Sep 20 '23 at 17:00
  • @aviro I am trying to prepend text to a set of files with sed in a loop. I need some line breaks in the text. they are .vtt files for audio captions and I need to put some metadata text at the beginning/header of each file. the text is: – Bleakley Sep 20 '23 at 17:37
  • 2
    OK, please [edit] your question, show us an example of your input and then what output you want from the example. Also, please add your operating system: there are many versions of sed out there and telling us your operating system will tell us which one you are likely to use and also what other tools might be available. – terdon Sep 20 '23 at 17:41
  • Apart from the sed issues, this is a duplicate of How to prepend multiple lines to a file – Kusalananda Sep 20 '23 at 17:59

3 Answers3

0

It would be easier and more portable with perl:

perl -0777 -ni -e 'print qq(WEBVTT
Kind: captions
Language: eng
File Creation Date: 2023-08
$_)' -- *.vtt

sed scripts consist of a series of [<address>[,<address>]] <command> where <command>s are single characters such as s for substitute, b for branch, i for insert.

Here, the first non-whitespace character of your script is a W. Not many seds have a W command. Yours doesn't, but in those that have it such as GNU sed, that will not do what you want.

Here it seems you want to insert some text at the beginning of the file. So you could use the i sed command with 1 as the address, meaning it will only be run on the first line (but beware it won't be run at all if the file is empty (has no line 1)):

sed -i '' -e '1 i\
Kind: captions\
Language: eng\
File Creation Date: 2023-08' -- *.vtt

Here assuming the FreeBSD implementation of sed which you seem to be using.

With the GNU implementation of sed, that would have to be:

sed -si -e '1 i\
Kind: captions\
Language: eng\
File Creation Date: 2023-08' -- *.vtt

There, the backup suffix (here empty) must be affixed to the -i options and -s is needed for the line number to be reset between each file.

With GNU sed, if you have the text to insert in a header file, you can do:

sed -si -e '0 r header' -- *.vtt

(address 0 is a GNU extension and is only valid in address ranges and for the r command; that still does not prepend the header to empty files)

0

You could always just write whatever you want prepended in a here-document:

#!/usr/bin/env bash

tmp=$(mktemp) || exit trap 'rm -f -- "$tmp"; exit' EXIT

shopt -s nullglob for file in *.vtt ; do cat -- - "$file" <<- 'EOF' > "$tmp" && WEBVTT Kind: captions Language: eng File Creation Date: 2023-08 EOF cat -- "$tmp" > "$file" done

sed -i uses a temp file behind the scenes, the above just makes it explicit. The leading spaces in the here-document must be tab characters, not any other type of blanks.

Ed Morton
  • 31,617
  • @StéphaneChazelas regarding temp files - good point about file attributes, I updated my answer so they're retained. I thought about also having it create the temp files in the same directory as each file with mktemp -p but don't think it's worth it if performance is the remaining concern (let me know if there's something else about the directory choice). As for cat - good idea, also updated in my answer.. – Ed Morton Sep 22 '23 at 11:26
  • 1
    Overwriting the original file as opposed to replacing them with the modified copy as perl/sed do with -i has the advantage of preserving most of the metadata but comes with its own risks: it can upset the processes that have the file currently opened, and you can end up losing data if the filesystem becomes full for instance. – Stéphane Chazelas Sep 22 '23 at 11:37
  • @StéphaneChazelas do you have an existing answer somewhere or some other reference that shows how you would implement using a temp file to update the original using commands that don't do "inplace" editing? – Ed Morton Sep 22 '23 at 11:41
  • 1
    No, doing it portably and reliably is virtually imossible. I just use perl -i. That's far from being perfect as well. It breaks hardlinks and symlinks, doesn't cover all meta data and can fail to restore some like ownership. Both approaches have their pros and cons, and depending on context, one may be preferable over the other, I'm not saying your approach is bad, just pointing out the risks. – Stéphane Chazelas Sep 22 '23 at 11:51
  • @StéphaneChazelas Understood and I appreciate it, I just would've wanted to adopt the better approach for commands in general if it was already out there. – Ed Morton Sep 22 '23 at 11:54
0

This might be easier with awk. Put your prepend text lines in a file (let's call it addit.txt) and use this loop:

$ for file in *.vtt ; do awk -i inplace 'NR==1{system ("cat addit.txt")}1' "$file"; done

This will add the short text to the top of every .vtt file.

user9101329
  • 1,004