Bash Scripting: Initialzing a path with a text file to a variable & iterating the list in the text file - linux

I'm currently learning the basics of bash scripting (coming from a Pythonic background) and It seems I've run into an interesting situation in which I need a bit of explanation with.
My goal is to create a Path variable that contains a text file. I use an if-statement to check whether the file exists in the directory. If it exists, It will print "It exists".
Then I created a for-loop to print out the elements in the file list. When I execute the program, there is no output and no errors being thrown.
Any help or explanation would be much appreciated! Here is the code below:
#!/bin/bash
veggieFile=/home/lynova/Potato/hewwo.txt
if [ -e veggieFile ]; then
echo "File exists"
for VEGGIE in $(cat veggieFile ); do
echo "Veggies are ${VEGGIE}"
done
fi

You are checking if the file veggieFile exists, not the file whose name is stored in the variable veggieFile. You need to expand the name:
if [ -e "$veggieFile" ]; then
echo "File exists"
while IFS= read -r veggie; do
echo "Veggies are $veggie"
done < "$veggieFile"
fi
See Bash FAQ 001 for more information on why I used a while loop instead of a for loop. Also, all-caps names are reserved; use lower- or mixed-case names for your own variables.
A possible source of confusion might be in how quotes are used. In Python, veggieFile would be a variable whose value is used, while "veggieFile" is a literal string. In shell, though, all strings are literal strings; quotes are only used to escape the contained characters. For example, the quoted word "foo bar" is equivalent to the foo\ bar. You need to use $ to get the value of a variable.

Related

How to read a line that contains non-string command inside a file via bash

Below is a snapshot of a file called ".bashrc":
I'm beginner in bash and What i'm trying to do in bash is to check if the last two lines inside the file exist and correctly written like for example :
if [ export PATH=/opt/ads2/arm-linux64/bin:$PATH ]
then
echo "found system variable lines"
else
echo "systemvariables do not exists, please insert it in .bashrc"
fi
However, this doesn't seem to be trivial since the tow lines to be shared are not pure string lines.
Thanks in advance
Use grep to find stuff in file contents.
# if file .bashrc contains the line exactly export PATH=....
if grep -Fxq 'export PATH=/opt/ads2/arm-linux64/bin:$PATH' .bashrc ; then
echo "found system variable lines"
else
echo "systemvariables do not exists, please insert it in .bashrc"
fi
Read man grep and decide if you want or not the -F and -x options in grep. For sure research and learn regex - I recommend regex crosswords available on the net. Research also difference between single quoting and double quoting in shell. Remember to check scripts with http://shellcheck.net

Understanding and Clarification on AIX and Batch File

I am new to AIX and I have trouble understanding the codes stated in the shell script as shown below, I have a few questions.
if [ "$OutChlName" != "" ] ; then
echo START CHANNEL \($OutChlName\)
fi
For the first line, what does the "" mean, does it mean null?
\($OutChlName\) - is there any way to convert this to a batch file format.
Is it right to say that fi is the end tag of if?
Thank you.
The echo is only wanted when the variable OutChkName is filled.
The string is compared with an empty string.
When you read man test, you can find the alternative if [ -n "$OutChlName" ].
echo with double quotes
Within quotes you do not need the backslashes.
echo "START CHANNEL ($OutChlName)"
My echo behaves different when the OutChlName variable has special characters like newlines or *. I think my syntax is a slight bugfix, but when you do not want to change the original behaviour, you can use
echo "START CHANNEL ("$OutChlName")"
Using backslashes is also a valid syntax (batch format).
fi ends if
Also esac ends case, and done ends do (Odd, it should have been od).

Bash Script Properties File Using '.' in Variable Name

I'm new to bash scripting and have a question about using properties from a .properties file within a bash script.
I have seen a bash properties file that uses'.' between variable names, for example:
this.prop.one=someProperty
and I've seen them called from within a script like:
echo ${this.prop.one}
But when I try to set this property I get an error:
./test.sh: line 5: ${this.prop.one}: bad substitution
I can use properties if I do it without '.' in the variable names, and include the props file:
#!/bin/bash
. test.properties
echo ${this_prop_one}
I would really like to be able to use '.' in the variable names, and, if at all possible, not have to include . test.properties in the script.
Is this possible?
UPDATE:
Thanks for your answers! Well, then this is strange. I'm working with a bash script that looks like this (a service for glassfish):
#!/bin/bash
start() {
sudo ${glassfish.home.dir}/bin/asadmin start-domain domain1
}
...
...and there are property files like this (build.properties):
# glassfish
glassfish.version=2.1
glassfish.home.dir=${app.install.dir}/${glassfish.target}
...
So, there must be some way of doing this right? Are these maybe not considered 'variables' by definition if they're declared in a properties file? Thanks again.
Load them into an associative array. This will require your shell to be bash 4.x, not /bin/sh (which, even when a symlink to bash, runs in POSIX compatibility mode).
declare -A props
while read -r; do
[[ $REPLY = *=* ]] || continue
props[${REPLY%%=*}]=${REPLY#*=}
done <input-file.properties
...after which you can access them like so:
echo "${props[this.prop.name]}"
If you want to recursively look up references, then it gets a bit more interesting.
getProp__property_re='[$][{]([[:alnum:].]+)[}]'
getProp() {
declare -A seen=( ) # to prevent endless recursion
declare propName=$1
declare value=${props[$propName]}
while [[ $value =~ $getProp__property_re ]]; do
nestedProp=${BASH_REMATCH[1]}
if [[ ${seen[$nestedProp]} ]]; then
echo "ERROR: Recursive definition encountered looking up $propName" >&2
return 1
fi
value=${value//${BASH_REMATCH[0]}/${props[$nestedProp]}}
done
printf '%s\n' "$value"
}
If we have props defined as follows (which you could also get by running the loop at the top of this answer with an appropriate input-file.properties):
declare -A props=(
[glassfish.home.dir]='${app.install.dir}/${glassfish.target}'
[app.install.dir]=/install
[glassfish.target]=target
)
...then behavior is as follows:
bash4-4.4$ getProp glassfish.home.dir
/install/target
No can do. The bash manual says this about variable names:
name
A word consisting solely of letters, numbers, and underscores, and beginning with a letter or underscore. Names are used as shell variable and function names. Also referred to as an identifier.
Dots not allowed.
dot is not allowed to be variable name. so you cannot just simply source the property file.
What you can do is:
"parse" the file, not source it. E.g. with perl, awk or grep to get the value of interesting property name, and assign it to your shell var.
if you do want to set a var with dot in its name, you can use env 'a.b.c=xyz' and get the a.b.c from env output.

Make multiple copies of files with a shell script

I am trying to write a small shell script that makes the multiple copies of a file. I am able to take the file name as input but not the number of copies. Here is what I wrote. But I am unable to pass the NUMBER variable to for loop.
echo -n "Enter filename: "
read FILENAME
echo -n "Number of copies to be made: "
read NUMBER
for i in {2..$NUMBER}
do
cp -f $FILENAME ${FILENAME%%.*}"_"$i.csv
done
Unfortunately it doesn't work like that. Bash performs brace expansion before parameter expansion, so your brace will be expanded before $NUMBER is evaluated. See also the Bash Pitfall #33, which explains the issue.
One way to do this, using your code, would be:
for i in $(eval echo {2..$NUMBER})
do
# ...
done
Or, even shorter:
for i in $(seq 2 $NUMBER)
# ...
(thanks, Glenn Jackman!)
Note that typically, variables should be quoted. This is especially important for file names. What if your file is called foo bar? Then your cp -f would copy foo and bar since the arguments are split by whitespace.
So, do something like this:
cp -f "$FILENAME" "${FILENAME%%.*}_${i}.csv"
While it might not matter if your files don't contain whitespace, quoting variables is something you should do automatically to prevent any surprises in the future.

Shell - Write variable contents to a file

I would like to copy the contents of a variable (here called var) into a file.
The name of the file is stored in another variable destfile.
I'm having problems doing this. Here's what I've tried:
cp $var $destfile
I've also tried the same thing with the dd command... Obviously the shell thought that $var was referring to a directory and so told me that the directory could not be found.
How do I get around this?
Use the echo command:
var="text to append";
destdir=/some/directory/path/filename
if [ -f "$destdir" ]
then
echo "$var" > "$destdir"
fi
The if tests that $destdir represents a file.
The > appends the text after truncating the file. If you only want to append the text in $var to the file existing contents, then use >> instead:
echo "$var" >> "$destdir"
The cp command is used for copying files (to files), not for writing text to a file.
echo has the problem that if var contains something like -e, it will be interpreted as a flag. Another option is printf, but printf "$var" > "$destdir" will expand any escaped characters in the variable, so if the variable contains backslashes the file contents won't match. However, because printf only interprets backslashes as escapes in the format string, you can use the %s format specifier to store the exact variable contents to the destination file:
printf "%s" "$var" > "$destdir"
None of the answers above work if your variable:
starts with -e
starts with -n
starts with -E
contains a \ followed by an n
should not have an extra newline appended after it
and so they cannot be relied upon for arbitrary string contents.
In bash, you can use "here strings" as:
cat <<< "$var" > "$destdir"
As noted in the comment by Ash below, #Trebawa's answer (formulated in the same room as mine!) using printf is a better approach than cat.
All of the above work, but also have to work around a problem (escapes and special characters) that doesn't need to occur in the first place: Special characters when the variable is expanded by the shell. Just don't do that (variable expansion) in the first place. Use the variable directly, without expansion.
Also, if your variable contains a secret and you want to copy that secret into a file, you might want to not have expansion in the command line as tracing/command echo of the shell commands might reveal the secret. Means, all answers which use $var in the command line may have a potential security risk by exposing the variable contents to tracing and logging of the shell.
For variables that are already exported, use this:
printenv var >file
That means, in case of the OP question:
printenv var >"$destfile"
Note: variable names are case sensitive.
Warning: It is not a good idea to export a variable just for the sake of printing it with printenv. If you have a non-exported script variable that contains a secret, exporting it will expose it to all future child processes (unless unexported, for example using export -n).
If I understood you right, you want to copy $var in a file (if it's a string).
echo $var > $destdir
When you say "copy the contents of a variable", does that variable contain a file name, or does it contain a name of a file?
I'm assuming by your question that $var contains the contents you want to copy into the file:
$ echo "$var" > "$destdir"
This will echo the value of $var into a file called $destdir. Note the quotes. Very important to have "$var" enclosed in quotes. Also for "$destdir" if there's a space in the name. To append it:
$ echo "$var" >> "$destdir"
you may need to edit a conf file in a build process:
echo "db-url-host=$POSTGRESQL_HOST" >> my-service.conf
You can test this solution with running before export POSTGRESQL_HOST="localhost"

Resources