Create text file from bash script - bash

I'm playing around with a simpler way to make animated GIFs with captions using gifify (forked from jclem) using ffmpeg and it's captioning library. I tried adding a variable to my script, looking for the optional argument, but I can't even get it to create the temporary .srt file necessary.
Here's my script creating a .txt as proof of concept:
#!/bin/bash
while getopts "t" opt; do
case opt in
t) text=$OPTARG;;
esac
done
shift $(( OPTIND - 1 ))
subtitles=$1
#If there is text present, do this
if [ -z ${text} ]; then
#Make an empty txt file
cat >> /tmp/subs.txt
text=$subtitles
append ${text}
fi
I then run it with:
sh text.sh -t "This is my text"
The script runs and will echo out the string of text you put into the shell, but it won't add it to the new file. Any thoughts on what I'm doing wrong?

!/bin/bash
1) You need case $opt.
while getopts "t:" opt; do
case $opt in
t) text=$OPTARG;;
esac
done
shift $(( OPTIND - 1 ))
subtitles=$1
Then,
if [ -z "$text" ]; then #safer and just as long as the curly bracket version
#Make an empty txt file
: > /tmp/subs.txt #This is how you create an empty file
cat /dev/null > /tmp/subs.txt #A longer version of the same thing
#cat >> /tmp/subs.txt #This APPENDS standard input (STDIN) to /tmp/subs.txt
text="$subtitles"
#append ${text} #`append` isn't bash
echo "$subtitles" > /tmp/subs.txt #the .txt file will contain what's in $subtitles
fi
Edit:
#Etan Reisner makes a good point about the quotation marks.
1) You don't need them in text=$subtitles; bash handles this OK
2) You don't need them in your case in echo $subtitles either--echo works OK with multiple arguments, which is what a bare $subtitles expands to--but you'd better off putting them there too, to make it work for cases like:
a='-e hello\nworld'
echo "$a" #Without the qutoes, $a would get expanded and `-e` would get treated as a flag to `echo`
I thinks it's a good practice to quote variables in bash defensively and not rely on quirks like that in the assignment in 1) or echo's not distinguishing between echo hello world and echo "hello world".

The question is little unclear but here I believe your basic problem is how to create or append a file. Here is the way to create a new file or append it in a shell script. Hopefully this will help. You can use it the way you want ->
Creating a file ->
cat<<EOF>/tmp/subs.txt
${text}
EOF
OR
echo "${text}" >/tmp/subs.txt
Appending a file (note extra '>') ->
cat<<EOF>>/tmp/subs.txt
${text}
EOF
OR
echo "${text}" >>/tmp/subs.txt
The EOF sometimes doesn't work due to tab or white spaces if you dont keep your text left-aligned.
Also regarding "text=$subtitles"; you cannot do that operation after 'cat' so move it before 'cat' command.

Related

Adding new lines to multiple files

I need to add new lines with specific information to one or multiple files at the same time.
I tried to automate this task using the following script:
for i in /apps/data/FILE*
do
echo "nice weather 20190830 friday" >> $i
done
It does the job yet I wish I can automate it more and let the script ask me for to provide the file name and the line I want to add.
I expect the output to be like
enter file name : file01
enter line to add : IWISHIKNOW HOWTODOTHAT
Thank you everyone.
In order to read user input you can use
read user_input_file
read user_input_text
read user_input_line
You can print before the question as you like with echo -n:
echo -n "enter file name : "
read user_input_file
echo -n "enter line to add : "
read user_input_text
echo -n "enter line position : "
read user_input_line
In order to add line at the desired position you can "play" with head and tail
head -n $[$user_input_line - 1] $user_input_file > $new_file
echo $user_input_text >> $new_file
tail -n +$user_input_line $user_input_file >> $new_file
Requiring interactive input is horrible for automation. Make a command which accepts a message and a list of files to append to as command-line arguments instead.
#!/bin/sh
msg="$1"
shift
echo "$msg" | tee -a "$#"
Usage:
scriptname "today is a nice day" file1 file2 file3
The benefits for interactive use are obvious -- you get to use your shell's history mechanism and filename completion (usually bound to tab) but also it's much easier to build more complicated scripts on top of this one further on.
The design to put the message in the first command-line argument is baffling to newcomers, but allows for a very simple overall design where "the other arguments" (zero or more) are the files you want to manipulate. See how grep has this design, and sed, and many many other standard Unix commands.
You can use read statement to prompt for input,
read does make your script generic, but if you wish to automate it then you have to have an accompanying expect script to provide inputs to the read statement.
Instead you can take in arguments to the script which helps you in automation.. No prompting...
#!/usr/bin/env bash
[[ $# -ne 2 ]] && echo "print usage here" && exit 1
file=$1 && shift
con=$1
for i in `ls $file`
do
echo $con >> $i
done
To use:
./script.sh "<filename>" "<content>"
The quotes are important for the content so that the spaces in the content are considered to be part of it. For filenames use quotes so that the shell does not expand them before calling the script.
Example: ./script.sh "file*" "samdhaskdnf asdfjhasdf"

Unix Bash content of a file as argument stops at first line

I'm having an issue in something that seems to be a rookie error, but I can't find a way to find a solution.
I have a bash script : log.sh
which is :
#!/bin/bash
echo $1 >> log_out.txt
And with a file made of filenames (taken from the output of "find" which names is filesnames.txt and contains 53 lines of absolute paths) I try :
./log.sh $(cat filenames.txt)
the only output I have in the log_out.txt is the first line.
I need each line to be processed separately as I need to put them in arguments in a pipeline with 2 softwares.
I checked for :
my lines being terminated with /n
using a simple echo without writing to a file
all the sorts of cat filenames.txt or (< filenames.txt) found on internet
I'm sure it's a very dumb thing, but I can't find why I can't iterate more than one line :(
Thanks
It is because your ./log.sh $(cat filenames.txt) is being treated as one argument.
while IFS= read -r line; do
echo "$line";
done < filenames.txt
Edit according to: https://mywiki.wooledge.org/DontReadLinesWithFor
Edit#2:
To preserve leading and trailing whitespace in the result, set IFS to the null string.
You could simplify more and skip using explicit variable and use the default $REPLY
Source: http://wiki.bash-hackers.org/commands/builtin/read
You need to quote the command substitution. Otherwise $1 will just be the first word in the file.
./log.sh "$(cat filenames.txt)"
You should also quote the variable in the script, otherwise all the newlines will be converted to spaces.
echo "$1" >> log_out.txt
If you want to process each word separately, you can leave out the quotes
./log.sh $(cat filenames.txt)
and then use a loop in the script:
#!/bin/bash
for word in "$#"
do
echo "$word"
done >> log_out.txt
Note that this solution only works correctly when the file has one word per line and there are no wildcards in the words. See mywiki.wooledge.org/DontReadLinesWithFor for why this doesn't generalize to more complex lines.
You can iterate with each line.
#!/bin/bash
for i in $*
do
echo $i >> log_out.txt
done

shell parsing a line to look for a certain tag

I am planning to create a simple script to edit a file based on values stored within a properties file.
So essentially I am planning to loop through each line in the original file, when it comes across a certain tag within a line say "/#" it will get the text following the tag i.e. certs and then implement a function to parse through the properties file to get certain values and add them to the original file.
So for example the file would have the following line:
"/#certs"
I am not sure how best to search for the tag, I was planning to have an if to find the /# and then split the remaining text to get the string.
while read line
do
#need to parse line to look for tag
echo line >> ${NEW_FILE}
done < ${OLD_FILE}
Any help would e greatly appreciated
=====================================
EDIT:
My explanation was a bit poor; apologies. I am merely trying to get the text following the /# - i.e. I just want to get the string value that precedes it. I can then call a function based on what the text is.
You can use BASH regex capabilities:
while read line
do
if [[ "$line" =~ ^.*/#certs(.*)$ ]]; then
# do your processing here
# echo ${BASH_REMATCH[1]} is the part after /#certs
echo echo ${BASH_REMATCH[1]} >> ${NEW_FILE}
fi
done < ${OLD_FILE}
This is portable to Bourne shell and thus, of course, ksh and Bash.
case $line in
'/#'* ) tag="${line#/\#}" ;;
esac
To put it into some sort of context, here is a more realistic example of how you might use it:
while read line; do
case $line in
'/#'* ) tag="${line#/\#}" ;;
*) continue ;; # skip remainder of loop for lines without a tag
esac
echo "$tag"
# Or maybe do something more complex, such as
case $tag in
cert)
echo 'We have a cert!' >&2 ;;
bingo)
echo 'You are the winner.' >&2
break # terminate loop
;;
esac
done <$OLD_FILE >$NEW_FILE
For instance you can search for strings and manipulate them in one step using sed the stream editor.
echo $line | sed -rn 's:^.*/#(certs.+):\1:p'
This will print only the relevant parts after the /# of the relevant lines.

Check execute command after cheking file type

I am working on a bash script which execute a command depending on the file type. I want to use the the "file" option and not the file extension to determine the type, but I am bloody new to this scripting stuff, so if someone can help me I would be very thankful! - Thanks!
Here the script I want to include the function:
#!/bin/bash
export PrintQueue="/root/xxx";
IFS=$'\n'
for PrintFile in $(/bin/ls -1 ${PrintQueue}) do
lpr -r ${PrintQueue}/${PrintFile};
done
The point is, all files which are PDFs should be printed with the lpr command, all others with ooffice -p
You are going through a lot of extra work. Here's the idiomatic code, I'll let the man page provide the explanation of the pieces:
#!/bin/sh
for path in /root/xxx/* ; do
case `file --brief $path` in
PDF*) cmd="lpr -r" ;;
*) cmd="ooffice -p" ;;
esac
eval $cmd \"$path\"
done
Some notable points:
using sh instead of bash increases portability and narrows the choices of how to do things
don't use ls when a glob pattern will do the same job with less hassle
the case statement has surprising power
First, two general shell programming issues:
Do not parse the output of ls. It's unreliable and completely useless. Use wildcards, they're easy and robust.
Always put double quotes around variable substitutions, e.g. "$PrintQueue/$PrintFile", not $PrintQueue/$PrintFile. If you leave the double quotes out, the shell performs wildcard expansion and word splitting on the value of the variable. Unless you know that's what you want, use double quotes. The same goes for command substitutions $(command).
Historically, implementations of file have had different output formats, intended for humans rather than parsing. Most modern implementations have an option to output a MIME type, which is easily parseable.
#!/bin/bash
print_queue="/root/xxx"
for file_to_print in "$print_queue"/*; do
case "$(file -i "$file_to_print")" in
application/pdf\;*|application/postscript\;*)
lpr -r "$file_to_print";;
application/vnd.oasis.opendocument.*)
ooffice -p "$file_to_print" &&
rm "$file_to_print";;
# and so on
*) echo 1>&2 "Warning: $file_to_print has an unrecognized format and was not printed";;
esac
done
#!/bin/bash
PRINTQ="/root/docs"
OLDIFS=$IFS
IFS=$(echo -en "\n\b")
for file in $(ls -1 $PRINTQ)
do
type=$(file --brief $file | awk '{print $1}')
if [ $type == "PDF" ]
then
echo "[*] printing $file with LPR"
lpr "$file"
else
echo "[*] printing $file with OPEN-OFFICE"
ooffice -p "$file"
fi
done
IFS=$OLDIFS

Creating files with some content with shell script

I need to configure a server with a few files and I want to do it programmatically.
I need to create files say /home/a.config, /var/spool/b.config, /etc/c.config
Files above have some contents (multi lines).
I want to create ONE shell script which can create all three file with multiple lines (around 10).
I would like to know the how can I use CAT command to do that. (inside shell script).
I am looking something like this
echo " going to create /home/a.config"
cat "HOW CAN I HAVE MULTIPLE LINES HERE?? " > /home/a.config
thanks
You can use a here document:
cat <<EOF >filename
first line
second line
third line
EOF
You can place several of these in the same script.
file="/tmp/test.txt"
echo "Adding first line" > $file
echo "Adding first line replaced" > $file
echo "Appending second line " >> $file
echo "Appending third line" >> $file
cat $file
> to add/replace the content ( here actual content got replaced by the 2nd line)
>> to append
Result
Adding first line replaced
Appending second line
Appending third line
Like so:
#!/bin/bash
var="your text"
echo "simply put,
just so: $var" > a.config
For further info, see Input/Output part of abs.
Hope, this helps.
If you've got variables like $1 or $HOMEDIR in your text then these normally get evaluated and substituted with actual values. If you want to prevent these from getting substituted then you need to quote the opening limit string (EOF in example below) with single quote 'EOF', double quote "EOF" or precede it with backslash \EOF
Closing limit string stays as is. EOF
This is especially useful if you are writing shell scripts to a file.
cat << 'EOF' >/etc/rc.d/init.d/startup
case $1 in
start)
start
;;
stop)
stop
;;
restart)
stop
start
;;
status)
pid=$(tomcat_pid)
if [ -n "$pid" ]
then
echo "Tomcat is running with pid: $pid"
else
echo "Tomcat is not running"
fi
;;
esac
EOF
Refer Example 19.7 Parameter Substitution Turned off in Here Documents
>\#!/bin/bash
>
>var="your text" <br>
>echo "simply put, <br>
>just so: $var" > a.config
Note that you also need to escape out certain characters to avoid them interfering with what you're trying to do, for example $ ` and " will all break such a statement unless preceded with a backslash, i.e. \` \$ or \"
so if we define the following:
var="100"
the following would not behave as expected:
echo "simply put,
just "lend" me US$ $var" > a.config
but the following would work correctly:
echo "simply put,
just \"lend\" me US\$ $var" > a.config

Resources