How in OS/X to get 'seq' command-line functionality? - macos

I'm still on Snow Leopard (I know...) so forgive if this is fixed in one of the later versions of OS/X, but I want to do standard "seq" aka:
for i in `seq 1 100` ; do
cat /whatever > $i.txt ;
done
I thought installing GNU tools would do it, but apparently not.

On my mac both of these work (OS X 10.8.5)
Andreas-Wederbrands-MacBook-Pro:~ raven$ for i in {1..10}; do echo $i; done
1
2
3
4
5
6
7
8
9
10
Andreas-Wederbrands-MacBook-Pro:~ raven$ for i in `seq 1 10`; do echo $i; done
1
2
3
4
5
6
7
8
9
10

In Snow Leopard, you can use the jot command, which can produce sequential data like seq (and more, see the man page for details).
$ jot 5
1
2
3
4
5
$ jot 3 5
5
6
7

No need for a tool such as seq -- bash (like ksh and zsh) has syntax built-in:
# bash 3.x+
for ((i=0; i<100; i++)); do
...
done
...or, for bash 2.04+, zsh, and ksh93:
i=0; while ((i++ <= 100)); do
...
done
...or, for absolutely any POSIX-compliant shell:
while [ $(( ( i += 1 ) <= 100 )) -ne 0 ]; do
...
done
bash also supports expansions such as {0..100}, but that doesn't support variables as endpoints, whereas the for-loop syntax is more flexible.

Or, just add this to your bash profile:
function seq {
if [ $1 > $2 ] ; then
for ((i=$1; i<=$2; i++))
do echo $i
done
else
for ((i=$1; i>=$2; i--))
do echo $i
done
fi
}
It's not that hard.

Related

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.

Print all numbers between two given numbers

I'm working on a Bash script which that takes in two integers and outputs all the numbers in between the two. It would look something like this:
Input:
bash testScript 3 10
3
4
5
6
7
8
9
10
This is some code that I wrote that I thought would work but I haven't had much luck getting it to work yet.
read myvar
read myvar2
while [ $myvar -le myvar2 ]
do
echo $myvar
myvar=$(($myvar+1))
//timer in-between numbers
sleep .5
done
Bash supports c style for loops using a double parenthesis construct:
$ for ((x=3; x<=10; x++)); { echo $x; }
3
4
5
6
7
8
9
10
Or, brace expansion:
$ for i in {3..6}; do echo $i; done
3
4
5
6
Problem with brace expansion is you need to use eval to use variables...
A common GNU utility for this is seq but it is not POSIX, so may not be on every *nix. If you want to write a similar function in Bash, it would look like this:
my_seq ()
# function similar to seq but with integers
# my_seq [first [incr]] last
{
incr=1
start=1
if [[ $# -gt 2 ]]; then
start=$1
incr=$2
end=$3
elif [[ $# -gt 1 ]]; then
start=$1
end=$2
else
end=$1
fi
for ((x=start; x<=end; x+=incr)); { echo $x; }
}
Then call that with 1, 2 or 3 arguments:
$ my_seq 30 10 60
30
40
50
60
with brace expansion
$ echo {3..10} | tr ' ' '\n'
or for variables with eval
$ a=3; b=10; eval echo {$a..$b} | ...
or, if you have awk
$ awk -v s=3 -v e=10 'BEGIN{while(s<=e) print s++}'
Use positional parameters:
myvar=$1
myvar2=$2
while [ $myvar -le $myvar2 ]
do
echo $myvar
myvar=$(($myvar+1))
#timer in-between numbers
sleep .5
done
Output:
scottsmudger#ns207588:~ $ bash test 1 5
1
2
3
4
5
See http://www.tldp.org/LDP/abs/html/othertypesv.html
Your posted codes are not aligned properly, not sure if it's your actual codes. But only problem other than the alignment is you missed a $ for myvar2 in the while statment.
read myvar
read myvar2
while [ $myvar -le $myvar2 ]
do
echo $myvar
myvar=$(($myvar+1))
#//timer in-between numbers
sleep .5
done

How to use eval for a variable inside a for in using Bash?

When I try doing this:
a={0..10}
for i in $a
do
echo $i
done
The output is just: {0..10}
printf -v a "%d " {0..10}
for i in $a
do
echo $i
done
Output:
0
1
2
3
4
5
6
7
8
9
10
This is one of many situations when the traditional tool, seq, is more useful than bash's brace notation:
a=$(seq 0 10)
for i in $a
do
echo $i
done
The above produces:
0
1
2
3
4
5
6
7
8
9
10
An additional advantage of seq is that it works with variables, something that bash's braces won't do. For example:
$ count=5
$ seq 0 $count
0
1
2
3
4
5
Contrast the above with:
$ echo {0..$count}
{0..5}
The above failed because bash's brace notation does not accept variables for arguments.
Mac OSX
seq is available on OSX for versions 10.8+ and maybe also 10.7. On older versions, the similar, but not identical, BSD tool jot can be used instead.
You can use:
for i in $(eval "echo $a"); do echo $i; done
0
1
2
3
4
5
6
7
8
9
10
But better (safer) than eval alternative in BASH is this arithmetic direecitve:
for ((i=0; i<=10; i++)); do echo $i; done
Your code will work if you do
a=$(echo {0..10})
Instead of a={0..10}, which will be interpreted as a='{0..10}' literally.

msysgit: Git bash misses the `seq` command. Is there an alternative?

I use seq a lot in my simulation shell scripts. The Git bash does not provide it, thus I am looking for an alternative.
Is there an alternative to seq that is part of the commands supported by the Git bash?
Current Solution: Based on Ignacio's answer I wrote a little helper script that provides my legacy scripts with a simple seq function. I also noticed, when using echo {1..10} with variables, you need to use eval to get the sequence output instead of the unexpanded expression:
a=0; b=5
eval echo {$a..$b} # outputs 0 1 2 4 5
echo {$a..$b} # outputs {0..5}
Here is my new seq.sh:
#!/bin/bash
# check for the real seq and export a new seq if not found
# import this script via `source ./seq.sh`
#
hasSeq(){
which seq >/devnull 2>&1
}
mySeq(){
case $# in
1) eval echo {1..$1};;
2) eval echo {$1..$2};;
3) echo "seq(3) not supported" 1>&2;;
esac
}
if ! hasSeq; then
seq(){
mySeq $*
}
fi
hasSeq || export -f seq
Assuming the version of bash is recent enough, you could use brace expansion.
$ echo {1..16}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Or... you know...

Bourne Shell For i in (seq)

I want to write a loop in Bourne shell which iterates a specific set of numbers. Normally I would use seq:
for i in `seq 1 10 15 20`
#do stuff
loop
But seemingly on this Solaris box seq does not exist. Can anyone help by providing another solution to iterating a list of numbers?
try
for i in 1 10 15 20
do
echo "do something with $i"
done
else if you have recent Solaris, there is bash 3 at least. for example this give range from 1 to 10 and 15 to 20
for i in {1..10} {15..20}
do
echo "$i"
done
OR use tool like nawk
for i in `nawk 'BEGIN{ for(i=1;i<=10;i++) print i}'`
do
echo $i
done
OR even the while loop
while [ "$s" -lt 10 ]; do s=`echo $s+1|bc`; echo $s; done
You can emulate seq with dc:
For instance:
seq 0 5 120
is rewritten as:
dc -e '0 5 120 1+stsisb[pli+dlt>a]salblax'
Another variation using bc:
for i in $(echo "for (i=0;i<=3;i++) i"|bc); do echo "$i"; done
For the Bourne shell, you'll probably have to use backticks, but avoid them if you can:
for i in `echo "for (i=0;i<=3;i++) i"|bc`; do echo "$i"; done
#!/bin/sh
for i in $(seq 1 10); do
echo $i
done
I find that this works, albeit ugly as sin:
for i in `echo X \n Y \n Z ` ...
for i in `seq 1 5 20`; do echo $i; done
Result:
5
10
15
20
$ man seq
SEQ(1) User Commands SEQ(1)
NAME
seq - print a sequence of numbers
SYNOPSIS
seq [OPTION]... LAST
seq [OPTION]... FIRST LAST
seq [OPTION]... FIRST INCREMENT LAST

Resources