Bash iterating of each variable and adding number - bash

I am processing a 7 PCAP file by splitting the file based on MAC address this is fine but I have various variables where I want to iterate through
${macs[*]} - I have a list 10 of different MAC address I would like to iterate through them
${devices[0]} - I have a list of 10 devices e.g. Samsung, Phillips I want to add a number to each file
for pcf in $pcap_file
do
for mac in ${macs[*]}
do
echo "$mac" >&2
/usr/bin/tshark -r "$pcf" -Y "eth.addr eq $mac" -w ${devices[0]}.pcap
done
done
At the moment I am manually uncommenting/commenting them
macs=( d0:45:a8:00:67:5e )
macs=( 44:65:0d:56:cc:d3 )
macs=( 70:ee:50:34:34:43 )
devices=('Samsunghub_1' 'Samsunghub_2' 'Samsunghub_3' 'Samsunghub_4' 'Samsunghub_5' 'Samsunghub_6' 'Samsunghub_7')
devices=('Echo_1' 'Echo_2' 'Echo_3' 'Echo_4' 'Echo_5' 'Echo_6' 'Echo_7')
devices=('netamo_1' 'netamo_2' 'netamo_3' 'netamo_4' 'netamo_5' 'netamo_6' 'netamo_7')
I want to iterate through each PCAP file extract based on the MAC address then label each one based on the "devices" but adding a number at the end

I'm not entirely clear what you are doing based on your post, but if you are making counts of the items it may be more useful to structure the devices as an associative array. Then just increment the value as you increase your count.
declare -A AA_devices
AA_devices[Samsunghub]="7"
AA_devices[Echo]="7"
AA_devices[netamo]="7"
You'd presumably want to set each equal to zero to begin. Once incremented you could use that data to either create the arrays you have outlined above (by iterating over your associative array) or whatever you want.
If you are counting which device based on which MAC address, you could then set an if/then statement that increments the devices counts.
for mac in "${macs[#]}" ; do
if mac = xx:xx:xx:xx ; then
AA_devices[netamo]+=1
Otherwise, let me know where I've misunderstood and I'll try again!

Related

How can i save in a list/array first parts of a line and then sort them depending on the second part?

I have a school project that gives me several lines of string in a text like this:
team1-team2:2-1
team3-team1:2-2
etc
it wants me to determine what team won (or drew) and then make a league table with them, awarding points for wins/draws.
this is my first time using bash. what i did was save team1/team2 names in a variable and then do the same for goals. how should i make the table? i managed to make my script create a new file that saves in there all team names (And checking for no duplicates) but i dont know how to continue. should i make an array for each team saving in there their results? and then how do i implement the rankings, for example
team1 3p
team2 1p
etc.
im not asking for actual code, just a guide as to how i should implement it. is making a new file the right move? should i try making a new array with the teams instead? or something else?
The problem can be divided into 3 parts:
Read the input data into memory in a format that can be manipulated easily.
Manipulate the data in memory
Output the results in the desired format.
When reading the data into memory, you might decide to read all the data in one go before manipulating it. Or you might decide to read the input data one line at a time and manipulate each line as it is read. When using shell scripting languages, like bash, the second option usually results in simpler code.
The most important decision to make here is how you want to structure the data in memory. You normally want to avoid duplication of data, and you usually want a data structure that is easy to transform into your desired output. In this case, the most logical data structure is an associative array, using the team name as the key.
Assuming that you have to use bash, here is a framework for you to build upon:
#!/bin/bash
declare -A results
while IFS=':-' read team1 team2 score1 score2; do
if [ ${score1} -gt ${score2} ]; then
((results[${team1}]+=2))
elif [ ...next test... ]; then
...
else
...
fi
done < scores.txt
# Now you have an associative array containing the points for each team.
# You can either output it as it stands, or sort it by piping through the
# 'sort' command.
for key in $[!results[#]}; do
echo ...
done
I would use awk for this
AWK is an interpreted programming language(AWK stands for Aho, Weinberger, Kernighan) designed for text processing and typically used as a data extraction and reporting tool. AWK is used largely with Unix systems.
Using pure bash scripting is often messy for that kind of jobs.
Let me show you how easy it can be using awk
Input file : scores.txt
team1-team2:2-1
team3-team1:2-2
Code :
awk -F'[:-]' ' # set delimiters to ':' or '-'
{
if($3>$4){teams[$1] += 3} # first team gets 3 points
else if ($3<$4){teams[$2] += 3} # second team gets 3 points
else {teams[$1]+=1; teams[$2]+=1} # both teams get 1 point
}
END{ # after scanning input file
for(team in teams){
print(team OFS teams[team]) # print total points per team
}
}' scores.txt | sort -rnk 2 > ranking.txt # sort by nb of points
Output (ranking.txt):
team1 4
team3 1

how to display 3 random pictures out of 8 using bash

I have 8 images in a directory.
the path is /blabla.com/img.
I need to access this path and choose 3 out of 8 randomly and display those.
If 3 pics are the same, it should echo "yeeey".
Otherwise, "neeey" and record these responses in a text file.
I am not going to do your homework for you!
However I can give you some insight:
store your 8 file names in an array
call $RANDOM % 8 3 times and store the value in 3 index variables
use the 3 index variables to extract your 3 files
use sha256sum, sha512sum or md5sum to compute the signature of your images and store the result in 3 variables
compare the values of the 3 variables if they are the same echo "yeeey" else echo "neeey"
if on top of that you want to display the picture as written in your post you could call eog or other similar tool with the finename as parameter and of course in background, with a & at the end of the command call.
Good luck with your assignment and let me know if you need help!
let's array an array of distinct elements (for example 8):
array=({A..H})
(1) use RANDOM special variable modulo the number of elements to get a random number between 0 and number-1 inclusive
number=$((RANDOM%${#array[#]}))
the first random element is
first=${array[number]}
remove the element from array and reassign the array to reindex without gap (declare -p array to see)
unset array[number]
array=("${array[#]}")
restart from (1)

Checking if a word is already in a list (Bash)

I'm writing a small bash script and am trying to test is a newly generated word is already in a list of all previously made words.
This is what I'm working with now:
dict=("word1"... "word21") #there would be 21 words in here
prev_guesses=()
guess_creator() {
guess=""
for i in {1..5} ;
do
guess_num=$( shuf -i 0-21 -n 1 )
guess+="${dict[$guess_num]}"
done
# using recursion to take another guess
if [ $guess (is in) $prev_guesses] ; then
guess_creator
else
prev_guess+=($guess)
fi
}
I'm also not sure if recursion works like this in bash. If it doesn't, I'm asking here how to actually "unbreak" this code.The idea is to have this function constantly outputting a unique string every time it runs so I can use it later on in the script.
I have three questions:
How can I compare guess to the list prev_guesses and get a true or false output
How can I append guessed string to the list prev_guesses (I just checked it and it is just concatenating the strings together, I need a list like prev_guesses=("guess1" "guess2"...) - I may have solved this with the final edit.
Does this recursion in guess_creator work?
Associative Arrays
Since you are only interested in »is this word in the list or not?« but not in the order of entries, you could use an associative array (also known as dictionary or hash map) to store your words. Checking whether an entry is in such a map is very fast (time complexity O(1)):
declare -A oldGuesses=([word1]= [word2]= [word3]=)
if [[ "${oldGuesses[$guess]+1}" ]]; then
echo "$guess was already taken"
else
echo "$guess was not taken yet"
fi
You can add an entry to dict using
dict["newEntry"]=
Don't worry about the empty right hand side. Maps are normally used to store key-value pairs. Here we only use the keys (the things which are written inside the []).
Avoiding the list of guesses completely
You mentioned that you want to bruteforce and that the list could grow up to 4M entries. I would advise against using bash, but even more against storing all guesses at all (no matter what language you are using).
Instead, enumerate all possible guesses in an ordered way:
You want to create guesses which are five concatenated words?
Just create five for-loops:
for w1 in "${dict[#]}"; do
for w2 in "${dict[#]}"; do
for w3 in "${dict[#]}"; do
for w4 in "${dict[#]}"; do
for w5 in "${dict[#]}"; do
guess="$w1$w2$w3$w4$w5"
# do something with your guess here
done
done
done
done
done
Benefits of this approach over your old approach:
Don't have to store 4M guesses.
Don't have to search through 4M guesses whenever taking a new guess.
Guarantees that the same guess is not picked over and over again.
Terminates when all possible guesses are made.
There's nothing like that in bash for arrays (Socowi's idea of using Associative Array is better), you would have to iterate through the list again, or maybe try to use grep or something
to refer to all the elements of an array you need the syntax ${prev_guesses[*]}
so you can concatenate with something like
prev_guesses=(${prev_guesses[*]} $guess)
Spaces in your words would make it all more complicated
It should do. BUT....
That's the hard way. If you want to avoid repeating guesses, better to take out each guess from the array when you take it, so you can't take it again.
Easier still is to use the shuf commmand to do everything
guess=($( shuf -e ${dict[*]} -n 5))
shuffle your words and take the first five

Increment Serial Number using EXIF

I am using ExifTool to change the camera body serial number to be a unique serial number for each image in a group of images numbering several hundred. The camera body serial number is being used as a second place, in addition to where the serial number for the image is in IPTC, to put the serial number as it takes a little more effort to remove.
The serial number is in the format ###-###-####-#### where the last four digits is the number to increment. The first three groups of digits do not change for each batch I run. I only need to increment that last group of digits.
EXAMPLE
I if I have 100 images in my first batch, they would be numbered:
811-010-5469-0001, 811-010-5469-0002, 811-010-5469-0003 ... 811-010-5469-0100
I can successfully drag a group of images onto my ExifTool Shortcut that has the values
exiftool(-SerialNumber='001-001-0001-0001')
and it will change the Exif SerialNumber Tag on the images, but have not been successful in what to add to this to have it increment for each image.
I have tried variations on the below without success:
exiftool(-SerialNumber+=001-001-0001-0001)
exiftool(-SerialNumber+='001-001-0001-0001')
I realize most likely ExifTool is seeing these as numbers being subtracted in the first line and seeing the second line as a string. I have also tried:
exiftool(-SerialNumber+='1')
exiftool(-SerialNumber+=1)
just to see if I can even get it to increment with a basic, single digit number. This also has not worked.
Maybe this cannot be incremented this way and I need to use ExifTool from the command line. If so, I am learning the command line/powershell (Windows), but am still weak in this area and would appreciate some pointers to get started there if this is the route I need to take. I am not afraid to use the command line, just would need a bit more hand holding then normal for a starting point. I also am learning Linux and could do this project from there but again, not afraid to use it, just would need a bit more hand holding to get it done.
I do program in PHP, JavaScript and other languages so code is not foreign to me. Just experience in writing it for the command-line.
If further clarification is needed, please let me know in the comments.
Your help and guidance is appreciated!
You'll probably have to go to the command line rather than rely upon drag and drop as this command relies upon ExifTool's advance formatting.
Exiftool "-SerialNumber<001-001-0001-${filesequence;$_=sprintf('%04d', $_+1 )}" <FILE/DIR>
If you want to be more general purpose and to use the original serial number in the file, you could use
Exiftool "-SerialNumber<${SerialNumber}-${filesequence;$_=sprintf('%04d', $_+1 )}" <FILE/DIR>
This will just add the file count to the end of the current serial number in the image, though if you have images from multiple cameras in the same directory, that could get messy.
As for using the command line, you just need to rename to remove the commands in the parens and then either move it to someplace in the command line's path or use the full path to ExifTool.
As for clarification on your previous attempts, the += option is used with numbers and with lists. The SerialNumber tag is usually a string, though that could depend upon where it's being written to.
If I understand your question correctly, something like this should work:
1..100 | % {
$sn = '811-010-5469-{0:D4}' -f $_
# apply $sn
}
or like this (if you iterate over files):
$i = 1
Get-ChildItem 'C:\some\folder' -File | % {
$sn = '811-010-5469-{0:D4}' -f $i
# update EXIF data of current file with $sn
$i++
}

Keeping track of path while recursively going through branches (more info in description )- Using Tcl

Background:
Writting script in Tcl
Running the script using a tool called IDSBatch from linux (centos) terminal
I have a system (.rdl file) that contains blocks, groups and registers.
Blocks can contain other blocks, groups, or registers. Whereas groups can only have registers and registers stand alone.
The problem I am having is I want to print out the "address" of each register i.e the name of the block(s), group and register associated with that specific register. For example:
______Block (a)______
| |
Block (b) reg(bob)
| |
group(tall) group(short)
| | |
reg(bill) reg(bobby) reg(burt)
In the end the output should be something along the lines of:
reg one: a.bob
reg two: a.b.tall.bill
reg three: a.b.tall.bobby
reg four: a.b.short.burt
The true problem comes from the fact that blocks can contain blocks. So the system will not always have one to three levels (one level would be Block--reg, two levels would be Block--Block--reg or Block ---group---reg and so on...)
I was leaning to some sort of recursive solution, where I would access the element say a block and get all of it's children (groups,blocks and regs) then I would use the same function to access it's children (unless it's a register). This way it can take care of any combination blocks groups and registers but then I'm stuck on how to keep track of the address of a specific register.
Thank you for taking the time in reading this and would appreciate any input or suggestions.
You could use a list for doing that.
Starting with an empty list, you append all address parts to it. If you come across a register, you can then construct the path from front to back. After every level of recursion, you remove the last element to get rid of the part you handled.
Example: you just came across the register bill. Then, your list is a -> b ->tall. To get the address, you iterate over the list and concatenate the nodes together, then appending bill to the resulting string.
So, your recursion function would be somewhat like
If the currently handled element is a register: Reconstruct the path.
If the currently handled element is not a register: Append the path element to the list, call the function with that list and remove the last element of that list.

Resources