This question already has answers here:
Creating an array from a text file in Bash
(7 answers)
Closed 2 years ago.
I'm trying to store each word from a file (named f1.txt) in an array (in bash), and then I want to print each element of the array. I tried something like this:
n=0
for varWord in $(cat f1.txt);
do
word[$n]=$varWord
$((n++))
done
for((i=0;i<n;i++));
do
echo $((word[i]))
done
I've also tried this (In fact, it was my first approach, as I would also prefer not to use an additional variable -- varWord, like I did above):
n=0
for word[$n] in $(cat f1.txt);
do
$((n++))
done
for((i=0;i<n;i++));
do
echo $((word[i]))
done
read -r -d '' -a words <inputfile.txt
That's all it needs.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
I am using a for f in $(cat /dir/file) nested loop with a conditional inside it to test if (($f=$z)), z being in the for z in $(cat /dir/file2) loop, where the f loop is nested.
I want to know if its possible to write to the line above the line where the $z string is in file2, if $z=$f.
each line in file2 is a 5 digit number like 07732, if there a line with 07732 in file I want to write that line above the line where that 07732 in file 2 is
What I've got right now is
for z in $(cat /dir/file2)
do
for f in $(cat /dir/file)
do
if (( $f=$z ))
then
(what im asking about)
i=0
break
else
i=1
fi
done
if ((i=1))
then
......
I know I've got syntax errors no need to correct those.
Sorry if I'm explaining it really poorly, please tell me if I need to explain in more detail
Delay the output of the matched line. And, as Charles Duffy pointed out in a commend, don't read lines with for. Also note that you will be reading the entire file2 for each line of file1. If file2 is of any significant size, this could bne very slow. If file2 is pretty small, read it into an array.
Also you should use meaningful variable names. This isn't 1970's BASIC where you are restricted to single characters.
Here is some sample code that incorporates most of that but leaves the inner while loop in place instead of using an array:
while read -r file1_line
do
while read -r -u 3 file2_line
do
if [[ $file2_line == $file1_line ]]
then
echo "some special putput"
i=0
break
else
i=1
fi
done 3< /path/to/file2
echo "NOW I'm echoing $file1_line"
if (( i == 1 ))
then
. . .
fi
done < /path/to/file1
This will echo the lines from file one, but when there's a match it will echo a string before the matched line.
The inner while loop uses an additional file descriptor in order to be able to read from a second file while the outer loop is reading from the STDIN file descriptor. This is so that multiple files can be read independently at the same time. The -u 3 tells read to use file descriptor 3. The 3< redirection puts the contents of file2 on file descriptor 3 so they can be read there.
This question already has answers here:
How do I set a variable to the output of a command in Bash?
(15 answers)
Copying a Bash array fails
(2 answers)
Closed 3 years ago.
I'm new to shell script and am having an issue with the shuf function.
This is my code
declare -a myarray=( 'A' 'B' 'C' 'D' 'E' 'F' )
myarray = $(shuf -e "${myarray[#]}")
echo "$myarray"
I make an array containing the six characters. I then shuffle them randomly, and print them out. My issue is that if I were to add another line, for example
echo ${myarray[2]}
This doesn't actually print the randomly sorted character in the 3rd position. Instead, it will always print 'C'. How can I actually save the sorted array? Do I need to make another array?
Thank you very much
Arrays in bash are defined with (). Bash is not statically typed, so setting myarray equal to some output of characters will do just that, making it a string you can echo with echo $myarray to see the full output.
You need to wrap your output in parens to make it clear to bash that your new myarray should also be an array:
myarray=($(shuf -e "${myarray[#]}"))
This question already has answers here:
How do I iterate over a range of numbers defined by variables in Bash?
(20 answers)
Closed 4 years ago.
I have a list of files to put through a for loop. They are named
FA2_00032.png, FA2_00033.png, etc
I have variables $imID which contains FA2 string, $startFrame which contains the start frame (e.g. 00034) and $endFrame which contains to end frame (e.g. 00894).
I have managed to get the list of relevant files using:
eval echo ${imageID}_{${startFrame}..${endFrame}}.png;
This outputs
FA2_00034 FA2_00035 FA2_00036 etc
But now I need to pass this to the for loop. That is,
for file in *the above list*
where *the above list* is the block quoted list above. $file should contain FA2_00034, FA2_00035, etc for use in the for loop.
Use a C-style for loop, not eval+{...}, for tasks like this. printf can be used to assemble the file name from the current integer. The syntax 10#$foo ensures that the value of $foo is treated as a decimal (not an octal) number if it includes leading 0s.
imageID=FA2
startFrame=00034
endFrame=00894
for ((i=10#startFrame; i<=10#$endFrame; i++)); do
printf -v file '%s_%05d.png' "$imageID" "$i"
echo "Operating on $file" >&2
done
I was given this text file, call stock.txt, the content of the text file is:
pepsi;drinks;3
fries;snacks;6
apple;fruits;9
baron;drinks;7
orange;fruits;2
chips;snacks;8
I will need to use bash-script to come up this output:
Total amount for drinks: 10
Total amount for snacks: 14
Total amount for fruits: 11
Total of everything: 35
My gut tells me I will need to use sed, group, grep and something else.
Where should I start?
I would break the exercise down into steps
Step 1: Read the file one line at a time
while read -r line
do
# do something with $line
done
Step 2: Pattern match (drinks, snacks, fruits) and do some simple arithmetic. This step requires that you tokenized each line which I'll leave an exercise for you to figure out.
if [[ "$line" =~ "drinks" ]]
then
echo "matched drinks"
.
.
.
fi
Pure Bash. A nice application for an associative array:
declare -A category # associative array
IFS=';'
while read name cate price ; do
((category[$cate]+=price))
done < stock.txt
sum=0
for cate in ${!category[#]}; do # loop over the indices
printf "Total amount of %s: %d\n" $cate ${category[$cate]}
((sum+=${category[$cate]}))
done
printf "Total amount of everything: %d\n" $sum
There is a short description here about processing comma separated files in bash here:
http://www.cyberciti.biz/faq/unix-linux-bash-read-comma-separated-cvsfile/
You could do something similar. Just change IFS from comma to semicolon.
Oh yeah, and a general hint for learning bash: man is your friend. Use this command to see manual pages for all (or most) of commands and utilities.
Example: man read shows the manual page for read command. On most systems it will be opened in less, so you should exit the manual by pressing q (may be funny, but it took me a while to figure that out)
The easy way to do this is using a hash table, which is supported directly by bash 4.x and of course can be found in awk and perl. If you don't have a hash table then you need to loop twice: once to collect the unique values of the second column, once to total.
There are many ways to do this. Here's a fun one which doesn't use awk, sed or perl. The only external utilities I've used here are cut, sort and uniq. You could even replace cut with a little more effort. In fact lines 5-9 could have been written more easily with grep, (grep $kind stock.txt) but I avoided that to show off the power of bash.
for kind in $(cut -d\; -f 2 stock.txt | sort | uniq) ; do
total=0
while read d ; do
total=$(( total+d ))
done < <(
while read line ; do
[[ $line =~ $kind ]] && echo $line
done < stock.txt | cut -d\; -f3
)
echo "Total amount for $kind: $total"
done
We lose the strict ordering of your original output here. An exercise for you might be to find a way not to do that.
Discussion:
The first line describes a sub-shell with a simple pipeline using cut. We read the third field from the stock.txt file, with fields delineated by ;, written \; here so the shell does not interpret it. The result is a newline-separated list of values from stock.txt. This is piped to sort, then uniq. This performs our "grouping" step, since the pipeline will output an alphabetic list of items from the second column but will only list each item once no matter how many times it appeared in the input file.
Also on the first line is a typical for loop: For each item resulting from the sub-shell we loop once, storing the value of the item in the variable kind. This is the other half of the grouping step, making sure that each "Total" output line occurs once.
On the second line total is initialized to zero so that it always resets whenever a new group is started.
The third line begins the 'totaling' loop, in which for the current kind we find the sum of its occurrences. here we declare that we will read the variable d in from stdin on each iteration of the loop.
On the fourth line the totaling actually occurs: Using shell arithmatic we add the value in d to the value in total.
Line five ends the while loop and then describes its input. We use shell input redirection via < to specify that the input to the loop, and thus to the read command, comes from a file. We then use process substitution to specify that the file will actually be the results of a command.
On the sixth line the command that will feed the while-read loop begins. It is itself another while-read loop, this time reading into the variable line. On the seventh line the test is performed via a conditional construct. Here we use [[ for its =~ operator, which is a pattern matching operator. We are testing to see whether $line matches our current $kind.
On the eighth line we end the inner while-read loop and specify that its input comes from the stock.txt file, then we pipe the output of the entire loop, which by now is simply all lines matching $kind, to cut and instruct it to show only the third field, which is the numeric field. On line nine we then end the process substitution command, the output of which is a newline-delineated list of numbers from lines which were of the group specified by kind.
Given that the total is now known and the kind is known it is a simple matter to print the results to the screen.
The below answer is OP's. As it was edited in the question itself and OP hasn't come back for 6 years, I am editing out the answer from the question and posting it as wiki here.
My answer, to get the total price, I use this:
...
PRICE=0
IFS=";" # new field separator, the end of line
while read name cate price
do
let PRICE=PRICE+$price
done < stock.txt
echo $PRICE
When I echo, its :35, which is correct. Now I will moving on using awk to get the sub-category result.
Whole Solution:
Thanks guys, I manage to do it myself. Here is my code:
#!/bin/bash
INPUT=stock.txt
PRICE=0
DRINKS=0
SNACKS=0
FRUITS=0
old_IFS=$IFS # save the field separator
IFS=";" # new field separator, the end of line
while read name cate price
do
if [ $cate = "drinks" ]; then
let DRINKS=DRINKS+$price
fi
if [ $cate = "snacks" ]; then
let SNACKS=SNACKS+$price
fi
if [ $cate = "fruits" ]; then
let FRUITS=FRUITS+$price
fi
# Total
let PRICE=PRICE+$price
done < $INPUT
echo -e "Drinks: " $DRINKS
echo -e "Snacks: " $SNACKS
echo -e "Fruits: " $FRUITS
echo -e "Price " $PRICE
IFS=$old_IFS