False Dragon
False Dragon

Reputation: 167

Writing a Bash script that takes a text file as input and pipes the text file through several commands

I keep text files with definitions in a folder. I like to convert them to spoken word so I can listen to them. I already do this manually by running a few commands to insert some pre-processing codes into the text files and then convert the text to spoken word like so:

sed 's/\..*$/[[slnc 2000]]/' input.txt inserts a control code after first period

sed 's/$/[[slnc 2000]]/' input.txt" inserts a control code at end of each line

cat input.txt | say -v Alex -o input.aiff

Instead of having to retype these each time, I would like to create a Bash script that pipes the output of these commands to the final product. I want to call the script with the script name, followed by an input file argument for the text file. I want to preserve the original text file so that if I open it again, none of the control codes are actually inserted, as the only purpose of the control codes is to insert pauses in the audio file.

I've tried writing

#!/bin/bash 
FILE=$1
sed 's/$/ [[slnc 2000]]/' FILE -o FILE

But I get hung up immediately as it says sed: -o: No such file or directory. Can anyone help out?

Upvotes: 0

Views: 1481

Answers (2)

William Pursell
William Pursell

Reputation: 212404

If you just want to use foo.txt to generate foo.aiff with control characters, you can do:

#!/bin/sh

for file; do
  test "${file%.txt}" = "${file}" && continue
  sed -e 's/\..*$/[[slnc 2000]]/' "$file" | 
    sed -e 's/$/[[slnc 2000]]/' | 
    say -v Alex -o "${file%.txt}".aiff
done

Call the script with your .txt files as arguments (eg, ./myscript *.txt) and it will generate the .aiff files. Be warned, if say overwrites files, then this will as well. You don't really need two sed invocations, and the sed that you're calling can be cleaned up, but I don't want to distract from the core issue here, so I'm leaving that as you have it.

Upvotes: 1

petrus4
petrus4

Reputation: 614

This will:-

a} Make a list of your text files to process in the current directory, with find.

b} Apply your sed commands to each text file in the list, but only for the current use, allowing you to preserve them intact.

c} Call "say" with the edited files.

I don't have say, so I can't test that or the control codes; but as long as you have Ed, the loop works. I've used it many times. I learned it as a result of exposure to FORTH, which is a language that still permits unterminated loops. I used to have problems with remembering to invoke next at the end of the script in order to start it, but I got over that by defining my words (functions) first, in FORTH style, and then always placing my single-use commands at the end.

#!/bin/sh

next() {
[[ -s stack ]] && main
end
}

main() {
line=$(ed -s stack < edprint+.txt)
infile=$(cat "${line}" | sed 's/\..*$/[[slnc 2000]]/' | sed 's/$/[[slnc 2000]]/')
say "${infile}" -v Alex -o input.aiff
ed -s stack < edpop+.txt
next
}

end() {
rm -v ./stack
rm -v ./edprint+.txt
rm -v ./edpop+.txt
exit 0
}

find *.txt -type -f > stack

cat >> edprint+.txt << EOF
1
q
EOF

cat >> edpop+.txt << EOF
1d
wq
EOF

next

Upvotes: 1

Related Questions