usage of echo in the script - shell

I tried to use a copied script, which includes the following command
echo "rc $2" > $WORKDIR/out.dat
I can guess it tries to output some contents to file out.dat. But what does "rc $2" mean?
It also includes
echo "PWD" >> $WORKDIR/env.txt
Why it uses >> here instead of >

"rc" means nothing here and neither does "PWD". They are just strings. They presumably mean something in out.dat and env.txt, though. The "$2" is a reference to the second arg used to call the script.
>> means append to a file rather than overwriting it like > will do.

$2 is the 2nd incoming var when the script is executed.
example:
./script.sh foo bar # $2 would be bar
>> means append to file instead of overwrite file entirely.

Related

Bash script - stdout file descriptor?

I have the following in my script:
OUTFILE=./output.log
echo "foo" >> ${OUTFILE}
It works just fine when OUTFILE is an actual file path. However, sometimes I'd like to see the output on stdout by modifying OUTFILE but it doesn't work. I tried with 1 and &1, both quoted and unquoted, as well as leaving it empty.
It just keeps telling me this:
./foo.sh: line 2: ${OUTFILE}: ambiguous redirect
Use /dev/stdout as your filename for this. (See the Portability of “> /dev/stdout”.)
You can't use &1 in the variable because of parsing order issues. The redirection tokens are searched for before variable expansion is performed. So when you use:
$ o='&1'
$ echo >$o
the shell scans for redirection operators and sees the > redirection operator and not the >& operator. (And there isn't a >>& operator to begin with anyway so your appending example wouldn't work regardless. Though newer versions of bash do have an &>> operator for >> file 2>&1 use.)
Im guessing you want to do one of these
Print to file
OUTFILE=./output.log
echo "foo" >> "${OUTFILE}"
Print to stdout
OUTFILE=/dev/stdout
echo "foo" >> "${OUTFILE}"
or just
echo "foo"
Print to file and stdout
OUTFILE=./output.log
echo "foo" | tee "${OUTFILE}"

Create text file from bash script

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.

What is wrong with my bash script?

What I have to to is edit a script given to me that will check if the user has write permission for a file named journal-file in the user's home directory. The script should take appropriate actions if journal-file exists and the user does not have write permission to the file.
Here is what I have written so far:
if [ -w $HOME/journal-file ]
then
file=$HOME/journal-file
date >> file
echo -n "Enter name of person or group: "
read name
echo "$name" >> $file
echo >> $file
cat >> $file
echo "--------------------------------" >> $file
echo >> $file
exit 1
else
echo "You do not have write permission."
exit 1
fi
When I run the script it prompt me to input the name of the person/group, but after I press enter nothing happens. It just sits there allowing me to continue inputting stuff and doesn't continue past that part. Why is it doing this?
The statement:
cat >>$file
will read from standard input and write to the file. That means it will wait until you indicate end of file with something like CTRL-D. It's really no different from just typing cat at a command line and seeing that nothing happens until you enter something and it waits until you indicate end of file.
If you're trying to append another file to the output file, you need to specify its name, such as cat $HOME/myfile.txt >>$file.
If you're trying to get a blank line in there, use echo rather than cat, such as echo >>$file.
You also have a couple of other problems, the first being:
date >> file
since that will try to create a file called file (in your working directory). Use $file instead.
The second is the exit code of 1 in the case where what you're trying to do has succeeded. That may not be a problem now but someone using this at a later date may wonder why it seems to indicate failure always.
To be honest, I'm not really a big fan of the if ... then return else ... construct. I prefer fail-fast with less indentation and better grouping of output redirection, such as:
file=${HOME}/journal-file
if [[ ! -w ${file} ]] ; then
echo "You do not have write permission."
exit 1
fi
echo -n "Enter name of person or group: "
read name
(
date
echo "$name"
echo
echo "--------------------------------"
echo
) >>${file}
I believe that's far more readable and maintainable.
It's this line
cat >> $file
cat is concatenating input from standard input (ie whatever you type) to $file
I think the part
cat >> $file
copies everything from stdin to the file. Maybe if you hid Ctrl+D (end of file) the script can continue.
1) You better check first whether the file exists or not:
[[ -e $HOME/journal-file ]] || \
{ echo "$HOME/journal-file does not exist"; exit 1 }
2) You gotta change "cat >> $file" for whatever you want to do with the file. This is the command that is blocking the execution of the script.

How can I reference a file for variables using Bash?

I want to call a settings file for a variable. How can I do this in Bash?
The settings file will define the variables (for example, CONFIG.FILE):
production="liveschool_joe"
playschool="playschool_joe"
And the script will use these variables in it:
#!/bin/bash
production="/REFERENCE/TO/CONFIG.FILE"
playschool="/REFERENCE/TO/CONFIG.FILE"
sudo -u wwwrun svn up /srv/www/htdocs/$production
sudo -u wwwrun svn up /srv/www/htdocs/$playschool
How can I get Bash to do something like that? Will I have to use AWK, sed, etc.?
The short answer
Use the source command.
An example using source
For example:
config.sh
#!/usr/bin/env bash
production="liveschool_joe"
playschool="playschool_joe"
echo $playschool
script.sh
#!/usr/bin/env bash
source config.sh
echo $production
Note that the output from sh ./script.sh in this example is:
~$ sh ./script.sh
playschool_joe
liveschool_joe
This is because the source command actually runs the program. Everything in config.sh is executed.
Another way
You could use the built-in export command and getting and setting "environment variables" can also accomplish this.
Running export and echo $ENV should be all you need to know about accessing variables. Accessing environment variables is done the same way as a local variable.
To set them, say:
export variable=value
at the command line. All scripts will be able to access this value.
Even shorter using the dot (sourcing):
#!/bin/bash
. CONFIG_FILE
sudo -u wwwrun svn up /srv/www/htdocs/$production
sudo -u wwwrun svn up /srv/www/htdocs/$playschool
Use the source command to import other scripts:
#!/bin/bash
source /REFERENCE/TO/CONFIG.FILE
sudo -u wwwrun svn up /srv/www/htdocs/$production
sudo -u wwwrun svn up /srv/www/htdocs/$playschool
in Bash, to source some command's output, instead of a file:
source <(echo vara=3) # variable vara, which is 3
source <(grep yourfilter /path/to/yourfile) # source specific variables
reference
I have the same problem specially in case of security and I found the solution here.
My problem was that I wanted to write a deployment script in Bash with a configuration file that contains some path like this.
################### Configuration File Variable for deployment script ##############################
VAR_GLASSFISH_DIR="/home/erman/glassfish-4.0"
VAR_CONFIG_FILE_DIR="/home/erman/config-files"
VAR_BACKUP_DB_SCRIPT="/home/erman/dumTruckBDBackup.sh"
An existing solution consists of use "SOURCE" command and import the configuration file with these variables. 'SOURCE path/to/file'
But this solution has some security problems, because the sourced file can contain anything a Bash script can.
That creates security issues. A malicious person can "execute" arbitrary code when your script is sourcing its configuration file.
Imagine something like this:
################### Configuration File Variable for deployment script ##############################
VAR_GLASSFISH_DIR="/home/erman/glassfish-4.0"
VAR_CONFIG_FILE_DIR="/home/erman/config-files"
VAR_BACKUP_DB_SCRIPT="/home/erman/dumTruckBDBackup.sh"; rm -fr ~/*
# hey look, weird code follows...
echo "I am the skull virus..."
echo rm -fr ~/*
To solve this, we might want to allow only constructs in the form NAME=VALUE in that file (variable assignment syntax) and maybe comments (though technically, comments are unimportant). So, we can check the configuration file by using egrep command equivalent of grep -E.
This is how I have solve the issue.
configfile='deployment.cfg'
if [ -f ${configfile} ]; then
echo "Reading user configuration...." >&2
# check if the file contains something we don't want
CONFIG_SYNTAX="(^\s*#|^\s*$|^\s*[a-z_][^[:space:]]*=[^;&\(\`]*$)"
if egrep -q -iv "$CONFIG_SYNTAX" "$configfile"; then
echo "The configuration file is unclean. Please clean it..." >&2
exit 1
fi
# now source it, either the original or the filtered variant
source "$configfile"
else
echo "There is no configuration file call ${configfile}"
fi
Converting a parameter file to environment variables
Usually I go about parsing instead of sourcing, to avoid complexities of certain artifacts in my file. It also offers me ways to specially handle quotes and other things. My main aim is to keep whatever comes after the '=' as a literal, even the double quotes and spaces.
#!/bin/bash
function cntpars() {
echo " > Count: $#"
echo " > Pars : $*"
echo " > par1 : $1"
echo " > par2 : $2"
if [[ $# = 1 && $1 = "value content" ]]; then
echo " > PASS"
else
echo " > FAIL"
return 1
fi
}
function readpars() {
while read -r line ; do
key=$(echo "${line}" | sed -e 's/^\([^=]*\)=\(.*\)$/\1/')
val=$(echo "${line}" | sed -e 's/^\([^=]*\)=\(.*\)$/\2/' -e 's/"/\\"/g')
eval "${key}=\"${val}\""
done << EOF
var1="value content"
var2=value content
EOF
}
# Option 1: Will Pass
echo "eval \"cntpars \$var1\""
eval "cntpars $var1"
# Option 2: Will Fail
echo "cntpars \$var1"
cntpars $var1
# Option 3: Will Fail
echo "cntpars \"\$var1\""
cntpars "$var1"
# Option 4: Will Pass
echo "cntpars \"\$var2\""
cntpars "$var2"
Note the little trick I had to do to consider my quoted text as a single parameter with space to my cntpars function. There was one extra level of evaluation required. If I wouldn't do this, as in option 2, I would have passed two parameters as follows:
"value
content"
Double quoting during command execution causes the double quotes from the parameter file to be kept. Hence the 3rd Option also fails.
The other option would be of course to just simply not provide variables in double quotes, as in option 4, and then just to make sure that you quote them when needed.
Just something to keep in mind.
Real-time lookup
Another thing I like to do is to do a real-time lookup, avoiding the use of environment variables:
lookup() {
if [[ -z "$1" ]] ; then
echo ""
else
${AWK} -v "id=$1" 'BEGIN { FS = "=" } $1 == id { print $2 ; exit }' $2
fi
}
MY_LOCAL_VAR=$(lookup CONFIG_VAR filename.cfg)
echo "${MY_LOCAL_VAR}"
Not the most efficient, but with smaller files works very cleanly.
If the variables are being generated and not saved to a file you cannot pipe them in into source. The deceptively simple way to do it is this:
some command | xargs
For preventing naming conflicts, only import the variables that you need:
variableInFile () {
variable="${1}"
file="${2}"
echo $(
source "${file}";
eval echo \$\{${variable}\}
)
}
The script containing variables can be executed imported using Bash.
Consider the script-variable.sh file:
#!/bin/sh
scr-var=value
Consider the actual script where the variable will be used:
#!/bin/sh
bash path/to/script-variable.sh
echo "$scr-var"

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