I want to sort the input of the user with sort in a case (and function).
But I never used this before. Do I have to use an array or something?
For example the user does:
bash test.sh 50 20 35 50
Normally in my script this would happen:
ping c -1 "192.168.0.$i"
That results in
192.168.0.50
192.168.0.20
192.168.0.35
192.168.0.50
Now I want that the last numbers are sorted and also pinged from smallest to the biggest number like this: 20 35 50 and also that if you have 2 times the same number, the script only pings that number one time.
SortNumbers(){
}
...
case
-sort ) SortNumbers;;
esac
You can use this:
#!/bin/bash
array=($(printf '%s\n' "$#"|sort -nu))
echo ${array[#]}
If you run test.sh 34 1 45 1 5 6 6 6, it will give output:
1 5 6 34 45
Now you can use the variable $array with a for loop like:
for i in ${array[#]};do
#do something with $i
done
Explanation:
The arguments of the script is piped to the command sort and the output is assigned into an array named array. The options -n is for numerical sort and -u is for unique.
Assumed complete code for you (for clarification):
#!/bin/bash
array=($(printf '%s\n' "$#"|sort -nu))
for i in ${array[#]};do
ping -c -1 "192.168.0.$i"
done
Using a function:
sortNumbers(){
array=($(printf '%s\n' "$#"|sort -nu))
}
sortNumbers 43 1 2 8 2 4 98 45
echo ${array[#]} ##this is just a sample use, you can put for loop here
So you can declare an array array=($#) at the begining of your script. then call the sortNumbers function with the arguments (remember to exclude -sort from the argument) when needed to sort them (it will change the variable $array with sorted content). Put the for loop outside the function so it takes whatever in the variable $array (sorted or unsorted), that way you will have it your way (choice to do sort or not).
Try this:
#!/bin/bash
# 1. copy the scripts arguments into an array
array=($#)
# 2. Set internal field separator to newline
IFS=$'\n'
# 3. pass the array contents to sort's stdin using here-string
sorted=($(sort <<<"${array[*]}"))
# 4. pass the output of sort to uniq utility using the same technique
uniq=($(uniq <<<"${sorted[*]}"))
# 5. print the final array
printf "%s\n" "${uniq[#]}"
lcd047's shorter version:
IFS=$'\n' sorted=($(sort -nu <<<"$*"))
set "${sorted[#]}"
printf "%s\n" "$#"
Run result:
$> bash test.sh 3 2 1 45 45 3 4 4 4 1 1 1 1
1
2
3
4
45
Related
I would like to write a code that can generate 3 rows of 6 random numbers spaced out, which shuffle after a given time (0.5 seconds), and no new rows are created, basically 6 random numbers keep generating in 3 rows.
The code I have so far is:
echo " "
echo " "
echo " "
for i in {1..5};
do
for i in {1..1};
do
echo -ne " $(($RANDOM % 100)) $(($RANDOM % 100)) $(($RANDOM % 100)) $(($RANDOM % 100)) $(($RANDOM % 100)) $(($RANDOM % 100))\r"
done
sleep 0.5
done
However, when I try to add the second and third row to this, it doesn't seem to work the way I want it. A sample output could look like:
45 88 85 90 44 22
90 56 34 55 32 45
58 99 42 10 48 98
and between these numbers, new ones will generate, keeping only 6 columns and 3 rows. I have tried making matrix too but it didn't work for me.
I don't know if you have it finish, but continuing on from the comment, I would fill an indexed array with random values between 1-100, e.g.
#!/bin/bash
for ((i = 0; i < 18; i++)); do ## fill array with random values
a[i]=$(($RANDOM % 100 + 1))
done
What you would then want is a function you could call, passing the number of values in each row (so you can output a '\n' after those digits print) and then the array values as arguments to the function to read into a local array within the function (of course, you can just use the original array without worrying about passing elements as arguments, but I prefer using local values within function to preserve values in other scopes unchanged. For that your print function could be something like:
## function to print array in shuffled order, using tput for cursor control
prnarray() {
local n=$1
local al=( ${#:2} )
local c=0
for i in $(shuf -i 0-$((${#al[#]} - 1))); do
[ "$c" -gt '0' -a $((c % n)) -eq '0' ] && printf "\n"
printf " %3d" "${al[i]}"
((c++))
done
printf "\n"
tput cuu 6 ## tput is used for cursor control to move back to top
}
Then you really don't need much else bu a loop to print the array, sleep for some period of time and then call prnarray again to overlay the output with a new shuffle. e.g.
tput sc ## save cursor position
## call prnarray 3 times with 5 sec. delay between displays
declare -i c=0
while (( c < 3 )); do
prnarray 3 ${a[#]}
((c++))
sleep 5
done
tput rc ## restore cursor position
Example Use/Output
The array will print in the same spot every 5 seconds with the same elements shuffled to different positions within the array.
$ sh randomshuf.sh
33 30 34
86 98 48
94 89 80
50 57 34
11 45 57
80 42 22
Give it a shot and let me know if you have any questions.
Note: to make it 3x6 change the lines:
tput cuu 3
and
prnarray 6 ${a[#]}
With those changed your output would resemble:
$ sh randomshuf.sh
85 9 45 14 18 16
6 59 43 19 29 58
7 89 18 72 29 29
I would recommend you to avoid using the shell for this. The shell is great for automating system administration tasks - e.g interact with files, directories, shell commands -, but it is also great to keep people away from learning a 'real' and most powerful programming language.
python, ruby or perl can help you out.
if all you have is a hammer, everything looks like a nail.
e.g ruby
def print_random_numbers(num)
random_numbers = []
num.times do |n|
random_numbers << rand(100)
end
puts random_numbers.join(' ')
puts
end
while true
3.times do
print_random_numbers(6)
end
sleep 0.5
end
I'm not sure whether I really understand your problem, but:
This gets you 6 numbers taken at random from the range 1-100
numbers=$(shuf -i 1-100 -n 6)
echo $numbers
The numbers are selected without repetition. If you want repetition, use -r.
This gives you a permutation of the numbers drawn before:
echo $numbers | tr ' ' '\n' | shuf | xargs echo
I'm trying to sort the numbers on each line of a file individually. The numbers within one line are separated by tabs. (I used spaces but they're actually tabs.)
For example, for the following input
5 8 7 6
1 5 6 8
8 9 7 1
the desired output would be:
5 6 7 8
1 5 6 7
1 7 8 9
My attempt so far is:
let i=1
while read line
do
echo "$line" | tr " " "\n" | sort -g
cut -f $i fileName | paste -s >> tempFile$$
((++i))
done < fileName
This is the best I got - I'm sure it can be done in 6 characters with awk/sed/perl:
while read line
do
echo $(printf "%d\n" $line | sort -n) | tr ' ' \\t >> another-file.txt
done < my-input-file.txt
Using a few features that are specific to GNU awk:
$ awk 'BEGIN{ PROCINFO["sorted_in"] = "#ind_num_asc" }
{ delete(a); n = 0; for (i=1;i<=NF;++i) a[$i];
for (i in a) printf "%s%s", i, (++n<NF?FS:RS) }' file
5 6 7 8
1 5 6 8
1 7 8 9
Each field is set as a key in the array a. In GNU awk it is possible to specify the order in which the for (i in a) loop traverses the array - here, I've set it to do so in ascending numerical order.
Here is a bash script that can do it. It takes a filename argument or reads stdin, was tested on CentOS and assumes IFS=$' \t\n'.
#!/bin/bash
if [ "$1" ] ; then exec < "$1" ; fi
cat - | while read line
do
set $line
echo $(for var in "$#"; do echo $var; done | sort -n) | tr " " "\t"
done
If you want to put the output in another file run it as:
cat input_file | sorting_script > another_file
or
sorting_script input_file > another file
Consider using perl for this:
perl -ape '#F=sort #F;$_="#F\n"' input.txt
Here -a turns on automatic field splitting (like awk does) into the array #F, -p makes it execute the script for each line and print $_ each time, and -e specifies the script directly on the command line.
Not quite 6 characters, I'm afraid, Sean.
This should have been simple in awk, but it doen't quite have the features needed. If there had been an array $# corresponding to the fields $1, $2, etc., then the solution would have been awk '{asort $#}' input.txt, but sadly no such array exits. The loops required to move the fields into an array and out of it again make it longer than the bash version:
awk '{for(i=1;i<=NF;i++)a[i]=$i;asort(a);for(i=1;i<=NF;i++)printf("%s ",a[i]);printf("\n")}' input.txt
So awk isn't the right tool for the job here. It's also a bit odd that sort itself doesn't have a switch to control its sorting direction.
Using awk
$ cat file
5 8 7 6
1 5 6 8
8 9 7 1
$ awk '{c=1;while(c!=""){c=""; for(i=1;i<NF;i++){n=i+1; if($i>$n){c=$i;$i=$n;$n=c}}}}1' file
5 6 7 8
1 5 6 8
1 7 8 9
Better Readable version
awk '{
c=1
while(c!="")
{
c=""
for(i=1;i<NF;i++)
{
n=i+1
if($i>$n)
{
c=$i
$i=$n
$n=c
}
}
}
}1
' file
If you have ksh, you may try this
#!/usr/bin/env ksh
while read line ; do
set -s +A cols $line
echo ${cols[*]}
done < "input_file"
Test
[akshay#localhost tmp]$ cat test.ksh
#!/usr/bin/env ksh
cat <<EOF | while read line ; do set -s +A cols $line; echo ${cols[*]};done
5 8 7 6
1 5 6 8
8 9 7 1
EOF
[akshay#localhost tmp]$ ksh test.ksh
5 6 7 8
1 5 6 8
1 7 8 9
I have a txt file with some lines such as:
a
b
c
f
e
f
1
2
3
4
5
6
now I want to random lines and print it to another txt file for example:
f
6
e
1
and so on...
could any body help me?
I am new in bash scripting
You could use shuf (a part of GNU coreutils).
shuf inputfile > outfile
For example:
$ seq 10 | shuf
7
5
8
3
9
4
10
1
6
2
There is an option for that
sort -R /your/file.txt
Expanation
-R, --random-sort
sort by random hash of keys
Iterate over the file, outputting each line with a certain probability (in this example, with roughly a 10% chance for each line:
while read line; do
if (( RANDOM % 10 == 0 )); then
echo "$line"
fi
done < file.txt
(I say "roughly", because the value of RANDOM ranges between 0 and 32767. As such, there are slightly more values that will produce a remainder of 0-7 than there are that will produce a remainder of 8 or 9 when divided by 10. Other probabilities are have similar problems; you can fine-tune the expression to be more precise, but I leave that as an exercise to the reader.)
For less fortunates systems without GNU utils like BSD/OSX you can use this code:
for ((i=0; i<10; i++)); do
n=$((RANDOM%10))
sed $n'q;d' file
done
I have two text files A.txt and B.txt. Each line of A.txt
A.txt
100
222
398
B.txt
1 2 103 2
4 5 1026 74
7 8 209 55
10 11 122 78
What I am looking for is something like this:
for each line of A
search B;
if (the value of third column in a line of B - the value of the variable in A > 10)
print that line of B;
Any awk for doing that??
How about something like this,
I had some troubles understanding your question, but maybe this will give you some pointers,
#!/bin/bash
# Read intresting values from file2 into an array,
for line in $(cat 2.txt | awk '{print $3}')
do
arr+=($line)
done
# Linecounter,
linenr=0
# Loop through every line in file 1,
for val in $(cat 1.txt)
do
# Increment linecounter,
((linenr++))
# Loop through every element in the array (containing values from 3 colum from file2)
for el in "${!arr[#]}";
do
# If that value - the value from file 1 is bigger than 10, print values
if [[ $((${arr[$el]} - $val )) -gt 10 ]]
then
sed -n "$(($el+1))p" 2.txt
# echo "Value ${arr[$el]} (on line $(($el+1)) from 2.txt) - $val (on line $linenr from 1.txt) equals $((${arr[$el]} - $val )) and is hence bigger than 10"
fi
done
done
Note,
This is a quick and dirty thing, there is room for improvements. But I think it'll do the job.
Use awk like this:
cat f1
1
4
9
16
cat f2
2 4 10 8
3 9 20 8
5 1 15 8
7 0 30 8
awk 'FNR==NR{a[NR]=$1;next} $3-a[FNR] < 10' f1 f2
2 4 10 8
5 1 15 8
UPDATE: Based on OP's edited question:
awk 'FNR==NR{a[NR]=$1;next} {for (i in a) if ($3-a[i] > 10) print}'
and see how simple awk based solution is as compared to nested for loops.
Is there a Linux utility or a Bash command I can use to sort a space delimited string of numbers?
Here's a simple example to get you going:
echo "81 4 6 12 3 0" | tr " " "\n" | sort -g
tr translates the spaces delimiting the numbers, into carriage returns, because sort uses carriage returns as delimiters (ie it is for sorting lines of text). The -g option tells sort to sort by "general numerical value".
man sort for further details about sort.
This is a variation from #JamesMorris answer:
echo "81 4 6 12 3 0" | xargs -n1 | sort -g | xargs
Instead of tr, I use xargs -n1 to convert to new lines. The final xargs is to convert back, to a space separated sequence of numbers.
This is a variation on ghostdog74's answer that's too big to fit in a comment. It shows digits instead of names of numbers and both the original string and the result are in space-delimited strings (instead of an array which becomes a newline-delimited string).
$ s="3 2 11 15 8"
$ sorted=$(echo $(printf "%s\n" $s | sort -n))
$ echo $sorted
2 3 8 11 15
$ echo "$sorted"
2 3 8 11 15
If you didn't use the echo when setting the value of sorted, then the string has newlines in it. In that case echoing it without quotes puts it all on one line, but, as echoing it with quotes would show, each number would appear on its own line. This is the case whether the original is an array or a string.
# demo
$ s="3 2 11 15 8"
$ sorted=$(printf "%s\n" $s | sort -n)
$ echo $sorted
2 3 8 11 15
$ echo "$sorted"
2
3
8
11
15
$ s=(one two three four)
$ sorted=$(printf "%s\n" ${s[#]}|sort)
$ echo $sorted
four one three two
Using Bash parameter expansion (to replace spaces with newlines) we can do:
str="3 2 11 15 8"
sort -n <<< "${str// /$'\n'}"
# alternative
NL=$'\n'
str="3 2 11 15 8"
sort -n <<< "${str// /${NL}}"
If you actually have a space-delimited string of numbers, then one of the other answers provided would work fine. If your list is a bash array, then:
oldIFS="$IFS"
IFS=$'\n'
array=($(sort -g <<< "${array[*]}"))
IFS="$oldIFS"
might be a better solution. The newline delimiter would help if you want to generalize to sorting an array of strings instead of numbers.
Improving on Evan Krall's nice Bash "array sort" by limiting the scope of IFS to a single command:
printf "%q\n" "${IFS}"
array=(3 2 11 15 8)
array=($(IFS=$'\n' sort -n <<< "${array[*]}"))
echo "${array[#]}"
printf "%q\n" "${IFS}"
$ awk 'BEGIN{split(ARGV[1], numbers);for(i in numbers) {print numbers[i]} }' \
"6 7 4 1 2 3" | sort -n
I added this to my .zshrc (or .bashrc) file:
#sort a space-separated list of words (e.g. a list of HTML classes)
sortwords() {
echo $1 | xargs -n1 | sort -g | xargs
}
Call it from the terminal like this:
sortwords "banana date apple cherry"
# apple banana cherry date
Thanks to #FranMowinckel and others for inspiration.