Read and Tokenize from a file in Bash - bash

I want to read something from a file and tokenize it into several variables in Bash, but I'm not sure how.
Example:
Enter file path: foo/bar.txt
var1 = 'a'
var2 = 'b'
var3 = 'c'
var4 = 'd'
The contents of bar.txt would simply be "a,b,c,d". It would have to be a one line file. I was thinking of using grep somehow. Is there an easy way of doing this, or am I making things to complicated?

IFS=, read var1 var2 var3 var4 < bar.txt
Set field separator
Set input file
Set resultant variables

Related

Best way to alter a file in a bash script [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 2 years ago.
Improve this question
i have a file that have variables and values
objective: open the file and replace all id by input id
[FILE]
var1 = 2
id = 3
var3 = 5
id = 12
var4 = 5
and i can't replace the id values to new ones.
here's my code, any help or something will help. thanks
#!/bin/bash
filename=$1
uuid=$2
input="./$filename"
# awk -v find="id " -v field="5" -v newval="abcd" 'BEGIN {FS=OFS="="} {if ($1 == find) $field=newval; print $1}' $input
while IFS= read -r line
do
awk -v find="id " -v field="5" -v newval="abcd" 'BEGIN {FS=OFS="="} {if ($1 == find) $field=newval;}' $input
echo $line
done < "$input"
expected output
execute
./myscript.sh file.cnf 77
expected output:
[FILE]
var1 = 2
id = 77
var3 = 5
id = 77
var4 = 5
I think sed is the right tool for this. You can even use its -i switch and update the file in-place.
$ cat file.txt
var1 = 2
id = 3
var3 = 5
id = 12
var4 = 5
$ NEW_ID=1234
$ sed -E "s/(id\s*=\s*)(.+)/\1${NEW_ID}/g" file.txt
var1 = 2
id = 1234
var3 = 5
id = 1234
var4 = 5
The string inside the quotes is a sed script for substituting some text with different text, and its general form is s/regexp/replacement/flags where "regexp" stands for "regular expression".
In the above example, the script looks for the string "id = ..." with any number of spaces or tabs around the "=" character. I divided the regexp into 2 groups (using parentheses) because we only want to replace the part to the right of the "=" character, and I don't think sed allows partial substitutions, so as a workaround I used \1 in the "replacement", which inserts the contents of the 1st group. The ${NEW_ID} actually gets evaluated by the shell so the value of the variable ("1234") is already part of the string by the time sed processes it. The g at the end stands for "global" and is probably redundant in this case. It makes sure that all occurrences of the regex on every line will get replaced; otherwise sed would only replace the first occurrence on each line.
Not sure. Bash scripts are extremely sensitive. I'm guessing your touch is what is causing this issue for a couple of reasons.
First whenever you touch a file name is should not consist of an operand or prefix unliss it is part of the shell script and $filename is shell or inline block quote. Touch is usually used for binaries or high priority data objects.
Second I'd try changing input and adjusted to $done and instead of echoing the $line echo the entire script using esac or end if instead of a do while loop.

conversion of SQL IN clause to multile ORs

I have a string "VAR1 = ABCD AND VAR2 IN ('AB' , 'CD' , 'EF')"
I want to convert that into something like this
VAR1 = ABCD AND (VAR2 ='AB' OR VAR2= 'CD' OR VAR3 = 'EF')
what is the best method to handle this via an algorithm ?

How to iterate over text file having multiple-words-per-line using shell script?

I know how to iterate over lines of text when the text file has contents as below:
abc
pqr
xyz
However, what if the contents of my text file are as below,
abc xyz
cdf pqr
lmn rst
and I need to get values "abc" stored to one variable and"xyz" stored to another variable. How would I do that?
read splits the line by $IFS as many times as you pass variables to it:
while read var1 var2 ; do
echo "var1: ${var1} var2: ${var2}"
done
You see, if you pass var1 and var2 both columns go to separate variables. But note that if the line would contain more columns var2 would contain the whole remaining line, not just column2.
Type help read for more info.
If the delimiter is a space then you can do:
#!/bin/bash
ALLVALUES=()
while read line
do
ALLVALUES+=( $line )
done < "/path/to/your/file"
So after, you can just reference an element by ${ALLVALUES[0]} or ${ALLVALUES[1]} etc
If you want to read every word in a file into a single array you can do it like this:
arr=()
while read -r -a _a; do
arr+=("${a[#]}")
done < infile
Which uses -r to avoid read from interpreting backslashes in the input and -a to have it split the words (splitting on $IFS) into an array. It then appends all the elements of that array to the accumulating array while being safe for globbing and other metacharacters.
This awk command reads the input word by word:
awk -v RS='[[:space:]]+' '1' file
abc
xyz
cdf
pqr
lmn
rst
To populate a shell array use awk command in process substitution:
arr=()
while read -r w; do
arr+=("$w")
done < <(awk -v RS='[[:space:]]+' '1' file)
And print the array content:
declare -p arr
declare -a arr='([0]="abc" [1]="xyz" [2]="cdf" [3]="pqr" [4]="lmn" [5]="rst")'

Getting variable values from variable names listed in array in Bash

I'm trying to print values of multiple variables that are listed in a Bash array as evident in the minimal code example below.
#!/bin/bash
VAR1="/path/to/source/root"
VAR2="/path/to/target/root"
VAR3="50"
VARS=("VAR1" "VAR2" "VAR3")
for var in ${VARS[*]}; do
echo "value of $var is ${$var}"
done
This gives me an error
line 8: value of $var is ${$var}: bad substitution
I want the following output:
value of VAR1 is /path/to/source/root
value of VAR2 is /path/to/target/root
value of VAR3 is 50
My search on Google and SO was not very fruitful. Because of the indirection (i.e., var iterates over an array containing names of variables for which I want the values), I'm not able to precisely word my search. But any help is appreciated.
Use indirect reference as:
#!/bin/bash
VAR1="/path/to/source/root"
VAR2="/path/to/target/root"
VAR3="50"
VARS=("VAR1" "VAR2" "VAR3")
for var in ${VARS[*]}; do
echo "value of $var is ${!var}"
done
Output:
value of VAR1 is /path/to/source/root
value of VAR2 is /path/to/target/root
value of VAR3 is 50

Variable assignment when reading from file using cat

I have a txt file that is in the format below:
name1 path/to/some/directory
name2 path/to/some/other/directory
name3 path/to/some/directory
name4 path/to/some/other/directory
...
Here is the code I have written to read this file line by line:
NUM=1
for line in $(cat /path/to/my/file.txt); do
if [ $((NUM%2)) -eq 1 ]
then
name= $line #this line does not work
echo $line #while this line works just fine
else
sudo tar -cf /desired/path/$name.tar $line
fi
NUM=$((NUM+1))
done
This code actually reads file word by word, and it alternates between then and else of if statement. Once it assigns a value it has read to variable name (then part inside if), then it uses that variable in command that is performed in else part of if. (This is how I expect it to work.)
The problem that arises is that variable assignment in then part of if seems not to work, it sees word it has just read as command, and doesn't assign its value to variable. I tried to echo it and it works just fine.
Why name= $line variable assignment is not working?
Thank you for any suggestions, comments or answers.
The assignments in bash require no space around =.
Hence, you need to say:
name="$line"
^ ^
quotes!
This happens because anything happening after the declaration is considered a command. See for example this, that tries to define r to 2 and then echo 1 is executed:
$ r=2 echo 1
1
This is why it is also a good thing to quote the declaration: name="$line".
Regarding the parsing and definition of variables of the file, you can maybe use this approach:
declare $('s/\s\+/="/; s/$/"/' a)
This replaces the spaces in between the first and second word by =" and the end of line with ". This way, name /path/ gets converted into name="/path/". By using declare, this command gets executed and makes variables be ready for use.
$ cat a
name1 aa
name2 rr
name5 hello
$ sed 's/\s\+/="/; s/$/"/' a
name1="aa"
name2="rr"
name5="hello"
$ declare $('s/\s\+/="/; s/$/"/' a)
So now you have the variables ready to use:
$ echo "$name5"
hello
And finally, note that this is equivalent (and better) than for line in $(cat /path/to/my/file.txt):
while IFS= read -r val1 val2 ...
do
... things ...
done < /path/to/my/file.txt

Resources