What will be the output of the following script?
#!/in/bash
secondLoop="A B C D E F G"
counter=0
for a in 6 7 8 9
do
for b in "$secondLoop"
do
let "counter+=1"
done
done
echo "This script has $counter iterations"
Options:
This script has 28 iterations.
This script has 21 iterations.
This script has 4 iterations.
This script has 0 iterations.
It should be 4 iterations, because there are quotes in
for b in "$secondLoop"
so that the variable is expanded to a single value (that included spaces).
Related
I wrote a simple code via bash script,
Code 1
for((i=1;i<=10;i++))
do
echo $i
i=$((i+1))
echo $i
i=$((i+2))
done
output of Code 1
1
2
5
6
9
10
Code 2
for i in {1..10}
do
echo $i
i=$((i+1))
echo $i
i=$((i+2))
done
output of Code 2
1
2
2
3
3
4
4
5
5
6
6
7
7
8
8
9
9
10
10
11
I'm just wondering, why outputs are not same ?
Thnx in advance
With in, the variable iterates over the list. You can change its value in the loop, but when the next iteration starts, the next value will be assigned to it, regardless of what value you assigned to it. (And I can't imagine any other behaviour: should the shell try to guess how far in the list you want to jump? What if the value is repeated, or not present in the list at all?)
With the C-style for, the variable is initialized, and at each iteration, its value is changed and the condition checked. There's no list of values, only the condition to end the loop.
These are not shorthand for each other: They're completely different code, and expected to behave in different ways.
{1..10} is just shorthand for 1 2 3 4 5 6 7 8 9 10; when you run for i in 1 2 3 4 5 6 7 8 9 10, you're explicitly assigning those exact values to i in turn, overwriting whatever else was previously there.
By contrast, for ((i=1; i<=10; i++)) is providing three separate statements: An initializer (i=1), telling it what to do to start the loop; a check (i<=10) to tell it how to determine whether the loop is finished; and an update (i++), telling it what to do between loop iterations. These are completely arbitrary commands, and you can put any arithmetic expression you want in these positions.
The for((i=1;i<=10;i++)) loop could be rewritten as such:
i=1 # for loop's 'i=1'
while [[ "${i}" -le 10 ]] # for loop's 'i<=10'
do
echo $i
i=$((i+1))
echo $i
i=$((i+2))
((i++)) # for loop's 'i++'
done
This generates ... you guessed it ...
1
2
5
6
9
10
Charles Duffy has already expanded the for i in {1..10} loop to show how that behaves.
Say I want to iterate over two lists of letters and numbers.
A B C D and seq 1 100.
How can I iterate over letters along with numbers but not as in nested for-loop? So it would be A1B2C3D4 A5B6C7D8 ...
What I've tried so far: nested for-loop and & done don't seem to be of any help, since they produce either A1 B1 C1 D1 A2 B2... or inconsistent results of parallel execution.
Also it feels like a very basic parallel loop, so no need for a detailed explanation or actual code: ANY ANSWER mentioning link to docs or the conventional name of such sequence would be immediately accepted.
The following script generates your expected output with a leading space:
Script
for i in {1..100}; do
IFS= read c
printf %s "$c$i"
done < <(yes $' A\nB\nC\n\D')
Output
A1B2C3D4 A5B6C7D8 A9B10C11D12 A13B14C15D16 A17B18C19D20 A21B22C23D24 A25B26C27D28 A29B30C31D32 A33B34C35D36 A37B38C39D40 A41B42C43D44 A45B46C47D48 A49B50C51D52 A53B54C55D56 A57B58C59D60 A61B62C63D64 A65B66C67D68 A69B70C71D72 A73B74C75D76 A77B78C79D80 A81B82C83D84 A85B86C87D88 A89B90C91D92 A93B94C95D96 A97B98C99D100
Explanation
To read the sequence 1 2 3 ... 100 in its full length, we need to repeat the sequence A B C D over and over again. yes is a command that repeats its argument ad infinitum. yes x prints
x
x
x
...
To let yes print something different in every line, we use a trick. $' A\nB\nC\nD' is a string that contains linebreaks ($'' is a so called bash ansi-c quote). yes $' A\nB\nC\nD' will print
A
B
C
D
A
B
...
Instead of printing to the console, we want to consume the text later. To this end, we could write yes ... | someCommand or someCommand < <(yes ...) which has some advantages over a pipe. The latter is called process substitution. Note that for ...; done is also just one command. The redirected stdin can be read from anywhere inside the for loop.
#!/bin/bash
# ASCII code for A
A=65
# Loop from 1 to 100
for ii in $( seq 1 100 )
do
# Compute ASCII code with using modulo
code=$(( (ii-1) % 4 + A ))
# Print letter
printf "\x$(printf %x $code)"
# Print number
echo $ii
done
I have a string with 3000 elements (NOT in series) in bash,
sections='1 2 4 ... 3000'
I am trying to split this string into x chunks of length n.
I want x to be typically between 3-10. Each chunk may not be of
the same length.
Each chunk is the input to a job.
Looking at https://unix.stackexchange.com/questions/122499/bash-split-a-list-of-files
and using bash arrays, my first attempt looks like this:
#! /bin/bash
nArgs=10
nChunkSize=10
z="0 1 2 .. 1--"
zs=(${z// / })
echo ${zs[#]}
for i in $nArgs; do
echo "Creating argument: "$i
startItem=$i*$nChunkSize
zArg[$i] = ${zs[#]:($startItem:$chunkSize}
done
echo "Resulting args"
for i in $nArgs; do
echo "Argument"${zArgs[$1]}
done
The above is far from working I'm afraid. Any pointers on the ${zs[#]:($startItem:$chunkSize} syntax?
For an input of 13 elements:
z='0 1 2 3 4 5 6 7 8 10 11 12 15'
nChunks=3
and nArgs=4
I would like to obtain an array with 3 elements, zs with content
zs[0] = '0 1 2 3'
zs[1] = '4 5 6 7'
zs[2] = '8 10 11 12 15'
Each zs will be used as arguments to subsequent jobs.
First note: This is a bad idea. It won't work reliably with arbitrary (non-numeric) contents, as bash doesn't have support for nested arrays.
output=( )
sections_str='1 2 4 5 6 7 8 9 10 11 12 13 14 15 16 3000'
batch_size=4
read -r -a sections <<<"$sections_str"
for ((i=0; i<${#sections[#]}; i+=batch_size)); do
current_pieces=( "${sections[#]:i:batch_size}" )
output+=( "${current_pieces[*]}" )
done
declare -p output # to view your output
Notes:
zs=( $z ) is buggy. For example, any * inside your list will be replaced with a list of filenames in the current directory. Use read -a to read into an array in a reliable way that doesn't depend on shell configuration other than IFS (which can be controlled scoped to just that one line with IFS=' ' read -r -a).
${array[#]:start:count} expands to up to count items from your array, starting at position start.
This question already has answers here:
Shell script read missing last line
(7 answers)
Closed 6 years ago.
I am having some difficulty understand why i'm not able to enter my while read j loop. The goal of my program is to enter files 1-5 and calculate the sum of the numbers in each file. The output i'm getting is showing me that the read j line is executed, however the while loop isn't entered. This doesn't make intuitive sense to me, since the while condition would be true if the line is read. Heres the output, and the snippet of program below:
in for loop
file1
in for loop
1 9 6 3 3 6
file2
in for loop
1 3 7 6 4 4
file3
in for loop
1 4 8 8 2 4
file4
in for loop
1 5 9 9 1 7
file5
Code:
for (( n=0;n<$columns;n++ ))
do
echo "in for loop"
echo $j
filename="file$counter"
echo "$filename"
#while reading line from current file, take the numbers and find the sum
while read j
do
echo "in while loop"
for number in $j
do
sum=$(($sum + $number))
done
echo "Sum: $sum"
done < $filename
counter=$(($counter + 1))
done
Solution: Add a newline character to the end of the line in each file, read looks for the newline character to recognize a line.
sh version : 1.14.7
#!/bin/sh
cpu_to_eth1=10
cpu_to_eth2=20
cpu_to_eth3=30
cpu_to_eth4=40
i=0
for i in 1 2 3 4
do
echo "value of the $i th varible is $cpu_to_eth$i"
done
it is not working properly,the output should be
value of the 1 th varible is 10
value of the 2 th varible is 20
value of the 3 th varible is 30
value of the 4 th varible is 40
Without requiring bash:
#!/bin/sh
cpu_to_eth1=10
cpu_to_eth2=20
cpu_to_eth3=30
cpu_to_eth4=40
for i in 1 2 3 4; do
eval echo "value of the $i th varible is "$"cpu_to_eth$i"
done
This should work in any POSIX shell (e.g. in dash, the default shell in Ubuntu).
The point is that you need two evaluations (for an indirect evaluation):
evaluate $i to get the name of the variable (cpu_to_eth$i)
evaluate the variable cpu_to_eth$i to get its actual value
The second-order evaluation needs a separate eval (or a bash-ism)
With bash, it's more appropriate to use an array here, rather than have multiple variables.
Example of an array:
cpu_to_eth_arr=( 10 20 30 40 )
for i in "${cpu_to_eth_arr[#]}"
do
echo "$i"
done
Another way, using an associative array:
cpu_to_eth[1]=10
cpu_to_eth[2]=20
cpu_to_eth[3]=30
cpu_to_eth[4]=40
for i in "${!cpu_to_eth[#]}"
do
echo "value of the $i th varible is ${cpu_to_eth[$i]}"
done
Using bash you can perform the Shell parameter expansion:
#!/bin/bash
cpu_to_eth1=10
cpu_to_eth2=20
cpu_to_eth3=30
cpu_to_eth4=40
i=0
for i in 1 2 3 4
do
val=cpu_to_eth${i} # prepare the variable
echo value of the $i th varible is ${!val} # expand it
done