Cat data_file n times [duplicate] - bash

This question already has answers here:
How do I iterate over a range of numbers defined by variables in Bash?
(20 answers)
Closed 3 years ago.
I have a file like this:
1
2
3
I need to copy data n times with an empty line after each copy. I used these commands
#!/bin/sh
num=$(sed -n '1 p' FILE.txt)
for i in {1.. $num }; do cat distance_k.txt >> distance.txt; done
n is a number taken from another file 'FILE.txt' (FILE.txt have form like this :
90
Abcbaahjfh
...
However, it copied only 4 times.
Could you please help me?
Thank you so much!
bash shell
Output
1
2
3
1
2
3
1
2
3
1
2
3
1
2
3
...

Use another kind of loop:
num=$(sed -n '1 p' FILE.txt)
for (( i=0; i<num; i++ ))
do
cat distance_k.txt
printf '\n'
done >distance.txt
If you like, you can use for (( i=1; i<=num; i++ )) instead of for (( i=0; i<num; i++ )). It's the same if not using the value of i.

Related

bash shell scripting transpose rows and columns

I need help transposing a file that just simply has some numbers in them with rows and columns. I can't use awk shell or perl so it makes it kind of hard. I've been working on it for a couple of hours and can't get it working correctly. I tried a couple of other things but this is what I have right now. It runs, but it doesn't print out anything, so that leads me to conclude that something is wrong within my code. Also if you dont know by transpose if a file had :
1 2 3
4 5 6
... it would then print out
1 4
2 5
3 6
Here is my code:
if [ $# -gt 2 ]
then
echo"System error">&2
exit 1
elif [[ $# -eq 2 && -e "$2" && -r "$2" ]]
then
while read -a line; do
for ((i=0; i < "${#line[#]}"; i++)); do
a[$i]="${a[$i]} ${line[$i]}"
done
done < $2
for ((i=0; i < ${#a[#]}; i++)); do
echo ${a[i]}
done
fi
If possible use awk:
Source (file.txt):
1 2 3
4 5 6
Result:
1 4
2 5
3 6
Oneline awk sctript:
awk '{ for (i=1; i<=NF; i++) a[i]= (a[i]? a[i] FS $i: $i) } END{ for (i in a) print a[i] }' file.txt
It works same with
1 2 3 1 4 7
4 5 6 -> 2 5
7 3 6
and with
1 2 3 4 1 5
5 6 7 -> 2 6
3 7
4
Instead of writing a Bash function, we could use rs, written specifically for reshaping matrices. This command does exactly what you ask for:
rs -T

Adding numbers in bash (works in zsh)

Why does ...
sum=0; for i in 1 2 3 4; do echo "$i" | sum=$((sum+i)); done; echo $sum
... work as expected in zsh but not in bash? Perhaps because of bash not supporting floating point arithmetic? I also tried ...
sum=0; for i in 1 2 3 4; do echo "$i" | awk '{sum+=$1}'; done; echo $sum
... but that doesn't work in neither (this is on macOS 10.14.2). I found several related questions (such as this or this) but this question still remained.
there is a wrong "|"
sum=0; for i in 1 2 3 4; do echo "$i" ; sum=$((sum+i)); done; echo $sum
1
2
3
4
10
The second example does not work as you are invoking awk every time the loop is repeated so the value of sum is not stored.

Calculating a loop size in bash script gives back different output than writing the size hard coded [duplicate]

This question already has answers here:
How do I iterate over a range of numbers defined by variables in Bash?
(20 answers)
Closed 5 years ago.
Is it possible to do something like this:
start=1
end=10
echo {$start..$end}
# Ouput: {1..10}
# Expected: 1 2 3 ... 10 (echo {1..10})
In bash, brace expansion happens before variable expansion, so this is not directly possible.
Instead, use an arithmetic for loop:
start=1
end=10
for ((i=start; i<=end; i++))
do
echo "i: $i"
done
OUTPUT
i: 1
i: 2
i: 3
i: 4
i: 5
i: 6
i: 7
i: 8
i: 9
i: 10
You should consider using seq(1). You can also use eval:
eval echo {$start..$end}
And here is seq
seq -s' ' $start $end
You have to use eval:
eval echo {$start..$end}
If you don't have seq, you might want to stick with a plain for loop
for (( i=start; i<=end; i++ )); do printf "%d " $i; done; echo ""
I normally just do this:
echo `seq $start $end`
Are you positive it has be BASH? ZSH handles this the way you want. This won't work in BASH because brace expansion happens before any other expansion type, such as variable expansion. So you will need to use an alternative method.
Any particular reason you need to combine brace and variable expansion? Perhaps a different approach to your problem will obviate the need for this.
use -s
ex:
seq -s ' ' 1 10
output: 1 2 3 4 5 6 7 8 9 10

How to sequence through sets of numbers in shell/bash

I would like to sequence through a set of numbers.
A simple example is sequence through 2 numbers, then sleep for 5 seconds before proceeding to the next 2.
For example:
1
2
(sleeping for 5 seconds)
3
4
(sleeping for 5 seconds)
Thank you in advance
You can use this for loop in bash:
for ((i=1; i<10; i+=2)); do echo $i $((i+1)); sleep 5; done
1 2
3 4
5 6
7 8
9 10
You could do this as well, using the modulo operator %:
declare -i max_value=10 # set it whatever you want
for ((i = 1; i < $max_value; i++)); do
printf "Number is: $i\n"
[[ $(( $i % 2 )) == 0 ]] && printf "Sleeping\n" && sleep 5 # sleep when the number is even
# do whatever...
done
Output:
Number is: 1
Number is: 2
Sleeping
Number is: 3
Number is: 4
Sleeping
Number is: 5
Number is: 6
Sleeping
Number is: 7
Number is: 8
Sleeping
Number is: 9
If your set of numbers is not a simple set of ascending integers like anubhava's response, you can do similar things.
For example, if your sequence is in a list, list.txt:
declare -ai a=($(<list.txt)); ## Read the sequence from list.txt into a
declare -i i l=${#a[#]};
for ((i=0; i<l; i+=2)); do
echo ${a[i]} ${a[i+1]};
sleep 5;
done;
If your sequence's numbers are in a string, like s='1,2,3,4,5,6,4,32,25,4,6,4', you could do declare -ai a=(${s//,/ }) to fill a with your numbers.

while loop | reverse counting n....8 7 6 5 4 3 2 1

I am trying to get an automatic reverse counting with user input.
The error is I keep getting the first number in the sequence twice.
#!/bin/bash
#Print decremental numbers based on user input
#n...8 7 6 5 4 3 2 1
echo "Input number"
read k
while test $k != 0
do
echo "$k"
k="$(( k - 1 ))"
done
Output:
[root#localhost standalone_scripts]# . ./decr.sh
Input number
5
5
4
3
2
1
as is pointed out in the comments, it's displaying the user input. if you don't like the way it's displayed, then you can switch the order of the subtraction with the echo.
#!/bin/bash
#Print decremental numbers based on user input
#n...8 7 6 5 4 3 2 1
echo "Input number"
read k
while test $k != 0
do
k="$(( k - 1 ))"
echo "$k"
done
Here's an idiomatic Bash reformulation of the script:
Using read -p prints the prompt string and the user's input on the same line, which makes misinterpreting the user's input as being part of the script's output less likely (which is what prompted the OP's question).
This is probably preferable to using read -s, which suppresses echoing the user's input as it is being typed.
Using a Bash-native, C-style arithmetic for loop (for (( ...; ...; ... ))) avoids the concerns about portability of the non-standard external seq utility.
For small input numbers, this is probably also faster than using seq, and also gives you the flexibility to act on each number individually.
For large input numbers, seq will be faster.
#!/usr/bin/env bash
read -p "Input number: " k
for (( i = k; i >= 1; --i )); do
echo "$i"
done
You could avoid the newline (and maybe add a :) after asking for user input, so that it's clear that the first 5 is user input.
Then here is an example that uses seq instead of while, though according to #andlrc, seq is less portable.
#!/bin/bash
#Print decremental numbers based on user input
#n...8 7 6 5 4 3 2 1
printf "Input number: "
read k
seq $k -1 1
Output:
$ ./decr.sh
Input number: 5
5
4
3
2
1
Edit:
#mklement0's answer is best.
read -p "Input number: " k
The read -s will turn off the echo effect of read
#!/bin/bash
#Print decremental numbers based on user input
#n...8 7 6 5 4 3 2 1
echo "Input number"
read -s k
while test $k != 0
do
echo "$k"
k="$(( k - 1 ))"
done
Your formatting is broken:
echo "Input number"; read k; while test $k != 0; do
echo "$k"; k="$(( k - 1 ))"; done
Seems to work fine for me:
Input number
3 <- this is what you enter
3 <- this is the beginning of the loop
2
1
try
#!/bin/bash
#Print decremental numbers based on user input
#n...8 7 6 5 4 3 2 1
echo "Input number"
read k
seq $((k-1)) -1 1
alternatively
#!/bin/bash
#Print decremental numbers based on user input
#n...8 7 6 5 4 3 2 1
echo "Input number"
read k
while test $k -gt 1; do
k=$((k-1))
echo "$k"
done
Using for and seq is the most elegant:
for i in `seq 5 -1 1`; do
echo $i
done
Output:
5
4
3
2
1

Resources