Trim table of text and store values as variables - linux

I am trying to write a script that can be run on my FreeNas (FreeBSD) box, that connects to an ESXi host via SSH and gracefully shuts down VMs. What I need to run for a list of VM IDs is:
vim-cmd vmsvc/power.shutdown VMID
Am after some assistance in filtering the output of the commands used to retrieve the IDs, and then passing it to the shutdown command.
The command to retrieve all VMs is:
vim-cmd vmsvc/getallvms
It outputs data like this:
Vmid Name File Guest OS Version Annotation
12 Eds-LINUX [Eds-FS-Datastore-1] Eds-LINUX/Eds-LINUX.vmx ubuntu64Guest vmx-13
13 Eds-RT [Eds-FS-Datastore-1] Eds-RT/Eds-RT.vmx freebsd64Guest vmx-13
14 Eds-DC [Eds-FS-Datastore-1] Eds-DC/Eds-DC.vmx windows9Server64Guest vmx-13
15 Eds-STEAM [Eds-FS-Datastore-1] Eds-STEAM/Eds-STEAM.vmx windows9_64Guest vmx-13
16 Eds-DL [Eds-FS-Datastore-1] Eds-DL/Eds-DL.vmx windows9Server64Guest vmx-13
17 Eds-RD [Eds-FS-Datastore-1] Eds-RD/Eds-RD.vmx windows9Server64Guest vmx-13
18 Eds-PLEX [Eds-FS-Datastore-1] Eds-PLEX/Eds-PLEX.vmx windows9Server64Guest vmx-13
19 Eds-MC [Eds-FS-Datastore-1] Eds-MC/Eds-MC.vmx windows9Server64Guest vmx-13
2 Eds-FS [Eds-ESXi-Datastore-1] Eds-FS/Eds-FS.vmx freebsd64Guest vmx-13
I have determined I can use a pipe into sed, to delete the first line, using:
vim-cmd vmsvc/getallvms | sed '1d'
I am then able to retrieve the ID of the VM I want to filter out, by using:
vim-cmd vmsvc/getallvms | awk '/Eds-FS.vmx/{print$1}'
This gives me the ID of 2. I am unclear however, how to store this in a variable for later use.
I need to know of a way to select just the first column from this data, and for each ID in the list, put it in an array. I then need to loop through the array and for each ID, run the below to get the power state of the VM:
vim-cmd vmsvc/power.getstate VMID
This outputs data like this, with a status of either power on or off:
Retrieved runtime info
Powered on
For each one that is found to be powered on, I need to store the VM ID in a second array to later pass to the shutdown command, except for where the ID is equal to that of the VM I want to power off.

Thanks to anubhava who gave me enough assistance to get something working (although probably not following standards or best practices)
I have this script saved on my ESXi host, which I connect to with SSH and trigger a run of:
freenasid=`vim-cmd vmsvc/getallvms | sed '1d' | awk '/Eds-FS.vmx/{print$1}'`
vmids=`vim-cmd vmsvc/getallvms | sed '1d' | awk '{print$1}'`
for vmid in $vmids
do
if [ $vmid != $freenasid ]
then
powerstate=`vim-cmd vmsvc/power.getstate $vmid | sed '1d'`
if [ "$powerstate" = "Powered on" ]
then
onvmids="$onvmids $vmid"
fi
fi
done
for vmid in $onvmids
do
vim-cmd vmsvc/power.shutdown $vmid
done
exit 0
This correctly shutsdown all running VMs.

To list IDs from first column use awk like this:
vim-cmd vmsvc/getallvms | awk 'NR>1{print $1}'
To store IDs in a shell array use:
readarray -t arr < <(vim-cmd vmsvc/getallvms | awk 'NR>1{print $1}')
To loop through array and run another command:
for id in "${arr[#]}"; do
vim-cmd vmsvc/power.getstate "$id"
done
To store one particular id use command substitution:
vmid1=$(vim-cmd vmsvc/getallvms | awk '/Eds-FS\.vmx/{print$1}')

Related

How can I fix my bash script to find a random word from a dictionary?

I'm studying bash scripting and I'm stuck fixing an exercise of this site: https://ryanstutorials.net/bash-scripting-tutorial/bash-variables.php#activities
The task is to write a bash script to output a random word from a dictionary whose length is equal to the number supplied as the first command line argument.
My idea was to create a sub-dictionary, assign each word a number line, select a random number from those lines and filter the output, which worked for a similar simpler script, but not for this.
This is the code I used:
6 DIC='/usr/share/dict/words'
7 SUBDIC=$( egrep '^.{'$1'}$' $DIC )
8
9 MAX=$( $SUBDIC | wc -l )
10 RANDRANGE=$((1 + RANDOM % $MAX))
11
12 RWORD=$(nl "$SUBDIC" | grep "\b$RANDRANGE\b" | awk '{print $2}')
13
14 echo "Random generated word from $DIC which is $1 characters long:"
15 echo $RWORD
and this is the error I get using as input "21":
bash script.sh 21
script.sh: line 9: counterintelligence's: command not found
script.sh: line 10: 1 + RANDOM % 0: division by 0 (error token is "0")
nl: 'counterintelligence'\''s'$'\n''electroencephalograms'$'\n''electroencephalograph': No such file or directory
Random generated word from /usr/share/dict/words which is 21 characters long:
I tried in bash to split the code in smaller pieces obtaining no error (input=21):
egrep '^.{'21'}$' /usr/share/dict/words | wc -l
3
but once in the script line 9 and 10 give error.
Where do you think is the error?
problems
SUBDIC=$( egrep '^.{'$1'}$' $DIC ) will store all words of the given length in the SUBDIC variable, so it's content is now something like foo bar baz.
MAX=$( $SUBDIC | ... ) will try to run the command foo bar baz which is obviously bogus; it should be more like MAX=$(echo $SUBDIC | ... )
MAX=$( ... | wc -l ) will count the lines; when using the above mentioned echo $SUBDIC you will have multiple words, but all in one line...
RWORD=$(nl "$SUBDIC" | ...) same problem as above: there's only one line (also note #armali's answer that nl requires a file or stdin)
RWORD=$(... | grep "\b$RANDRANGE\b" | ...) might match the dictionary entry catch 22
likely RWORD=$(... | awk '{print $2}') won't handle lines containing spaces
a simple solution
doing a "random sort" over the all the possible words and taking the first line, should be sufficient:
egrep "^.{$1}$" "${DIC}" | sort -R | head -1
MAX=$( $SUBDIC | wc -l ) - A pipe is used for connecting a command's output, while $SUBDIC isn't a command; an appropriate syntax is MAX=$( <<<$SUBDIC wc -l ).
nl "$SUBDIC" - The argument to nl has to be a filename, which "$SUBDIC" isn't; an appropriate syntax is nl <<<"$SUBDIC".
This code will do it. My test dictionary of words is in file file. It's a good idea to get all words of a given length first but put them in an array not in var. And then get a random index and echo it.
dic=( $(sed -n "/^.\{$1\}$/p" file) )
ind=$((0 + RANDOM % ${#dic[#]}))
echo ${dic[$ind]}
I am also doing this activity and I create one simple solution.
I create the script.
#!/bin/bash
awk "NR==$1 {print}" /usr/share/dict/words
Here if you want a random string then you have to run the script as per the below command from the terminal.
./script.sh $RANDOM
If you want the print any specific number string then you can run as per the below command from the terminal.
./script.sh 465
cat /usr/share/dict/american-english | head -n $RANDOM | tail -n 1
$RANDOM - Returns a different random number each time is it referred to.
this simple line outputs random word from the mentioned dictionary.
Otherwise as umläute mentined you can do:
cat /usr/share/dict/american-english | sort -R | head -1

Using printf to display an extracted string through grep and to be use as user input in a script

Good day,
This is kinda lenghty, Im hoping for the kind help of anybody who can support me on this simple problem (to others) but taking me almost forever to figure out.
I have this file (EOL.txt) which consists of the following sample lists:
35 - 5976
36 - 5976C0
53 - 5976C2
64 - 5976D0
69 - 43593
72 - 43593C0
Im using the following commands to extract the leftmost figure since this correspond to a routine number of another script:
grep 5976C2 EOL.txt | head -n1 | cut -d- -f1
After I acquired that number, I will input that along with the other data on another script (N.csh-syntax as follows) that will execute another one (Test.csh):
$./N.csh 53 XXXX.XX "01 02 03"
N.csh --> printf "$1\n$2\n$3\nYYYY\n1\nN\n" | /export/home/Script/Test.csh
What I want to do now is to incorporate the grep command to N.csh so that I wont have to do that separately. It should look like this:
$./N.csh 5976C2 XXXX.XX "01 02 03"
I tried the following commands but its not working.
grep $1 EOL.txt | head -n1 | cut -d- -f1 >> A ; set B=`cat A` ; printf %s "$B\n$2\n$3\n82869\n1\nN\n"
Im new to this stuff, any help will be highly appreciated.
Thanks a lot in advance.
Mike
You can use the following in the file N.csh:
set mynumber = `grep $1 EOL.txt | head -n1 | cut -d- -f1`
printf "$mynumber\n$2\n$3\n...
and then invoke N.csh like
./N.csh 5976C2 XXXX.XX "01 02 03"
Note that after set mynumber =, in the first line, there is a "backtick" - a reversed single quote. The shell executes the commands delimited by two backticks, takes the output, and puts it back in place of the original contents, so the first line turns into set mynumber = 53.

Copying content of 2 files in a new file by pairing the values in linux

There are supposed to be 2 files. One file has list of users and the other files has list of passwords.
User file has 5 users and password file also has 5 passwords. What I have to do here is create a new file and pair each user with all the available passwords in password file. For example: user 1 is to be paired with password 1, 2,3,4,5. user 2 is to be paired with password 1,2,3,4,5 and so on.
My question is:
1. What loop should be used here?
2. My head says for pairing we need to use nested for loop. is it correct?
3. I can somehow imagine the 1st part of copying the contents but I'm not able to picture how to pair them. So I need help and suggestions.
EDIT:
Sample input are the 2 files named Username and Password.
Username file:
User1
User2
..
User5
Password file:
Pass1
Pass2
..
Pass5
Expected output is:
User1-Pass1
User1-Pass2
..
User1-Pass5
User2-Pass1
User2-Pass2
..
User2-Pass5
and so on till we reach User5-Pass5
Thanks.
Ad 1., 2. Yes, nested loop required.
Let's expect one user per line and one password per line, then code would be:
> concat # delete file content
for usr in `cat file_with_users`
do
for pwd in `cat file_with_passwords`
do
echo "$usr-$pwd" >> result_file
done
done
If you write input sample and expected output, I can write something more specific.
always there is a better way
$ join -t, -j 99 users pwords | cut -d, -f2-
for example
$ echo u{1..5} | tr ' ' '\n' > users
$ echo p{1..5} | tr ' ' '\n' > pwords
$ join -t, -j 99 users pwords | cut -d, -f2-
u1,p1
u1,p2
u1,p3
u1,p4
u1,p5
u2,p1
u2,p2
...
u5,p4
u5,p5
for - delimiter change to join -t- -j 99 users pwords | cut -d- -f2-
This is using a fictional column (99th) to create a join between each rows, which is called cross-product. Since the key is missing in the first position of the output we need to cut it out at the end.
Do either of these help:
grep -Fwf file1 file2 or
awk 'NR==FNR{A[$1];next}$1 in A' file1 file2

Obtaining specific data from a very very long string unix

I'm doing a script in unix for obtaining specific data, after running a program it gives as an output a very huge string, for example: (is just a random example)
In this example, the null scorex: 34;hypothesis of "marginal homogeneity" would mean there was no effect of the treatment. From the above data, the McNemar scorex: 687;test statistic with Yates's continuity correction is scorex: 9;
and I like that whenever it finds the string "scorex: " it gives me the actual score: 34, 687 or 9, for this example.
Thank you
I forgot, my string is inside a variable called RESULTADO
You can use grep:
grep -oP 'scorex:\s?\K\d*' input
or
<command> | grep -oP 'scorex:\s?\K\d*'
For your example:
$ echo "In this example, the null scorex: 34;hypothesis of "marginal homogeneity" would mean there was no effect of the treatment. From the above data, the McNemar scorex: 687;test statistic with Yates's continuity correction is scorex: 9;" | grep -oP 'scorex:\s?\K\d*'
34
687
9
This can be solved via a regex. Considering the following pattern:
scorex: (\d+)
Using this pattern with grep would look like this:
grep -Eo "scorex: (\d+)" file_containing_string | cut -d: -f2
The output of this is for every capture the result:
34
687
9

How to insert shell variable inside awk command

I'm trying to write a script, In this script i'm passing a shell variable into an awk command, But when i run it nothing happens, i tried to run that line only in the shell, i found that no variable expansion happened like i expected. Here's the code :
1 #!/bin/bash
2
3 # Created By Rafael Adel
4
5 # This script is to start dwm with customizations needed
6
7
8 while true;do
9 datestr=`date +"%r %d/%m/%Y"`
10 batterystr=`acpi | grep -oP "([a-zA-Z]*), ([0-9]*)%"`
11 batterystate=`echo $batterystr | grep -oP "[a-zA-Z]*"`
12 batterypercent=`echo $batterystr | grep -oP "[0-9]*"`
13
14 for nic in `ls /sys/class/net`
15 do
16 if [ -e "/sys/class/net/${nic}/operstate" ]
17 then
18 NicUp=`cat /sys/class/net/${nic}/operstate`
19 if [ "$NicUp" == "up" ]
20 then
21 netstr=`ifstat | awk -v interface=${nic} '$1 ~ /interface/ {printf("D: %2.1fKiB, U: %2.1fKiB",$6/1000, $8/1000)}'`
22 break
23 fi
24 fi
25 done
26
27
28 finalstr="$netstr | $batterystr | $datestr"
29
30 xsetroot -name "$finalstr"
31 sleep 1
32 done &
33
34 xbindkeys -f /etc/xbindkeysrc
35
36 numlockx on
37
38 exec dwm
This line :
netstr=`ifstat | awk -v interface=${nic} '$1 ~ /interface/ {printf("D: %2.1fKiB, U: %2.1fKiB",$6/1000, $8/1000)}'`
Is what causes netstr variable not to get assigned at all. That's because interface is not replaced with ${nic} i guess.
So could you tell me what's wrong here? Thanks.
If you want to /grep/ with your variable, you have 2 choices :
interface=eth0
awk "/$interface/{print}"
or
awk -v interface=eth0 '$0 ~ interface{print}'
See http://www.gnu.org/software/gawk/manual/gawk.html#Using-Shell-Variables
it's like I thought, awk substitutes variables properly, but between //, inside regex ( or awk regex, depending on some awk parameter AFAIR), awk variable cannot be used for substitution
I had no issue grepping with variable inside an awk program (for simple regexp cases):
sawk1='repo\s+module2'
sawk2='#project2\s+=\s+module2$'
awk "/${sawk1}/,/${sawk2}/"'{print}' aFile
(Here the /xxx/,/yyy/ displays everything between xxx and yyy)
(Note the double-quoted "/${sawk1}/,/${sawk2}/", followed by the single-quoted '{print}')
This works just fine, and comes from "awk: Using Shell Variables in Programs":
A common method is to use shell quoting to substitute the variable’s value into the program inside the script.
For example, consider the following program:
printf "Enter search pattern: "
read pattern
awk "/$pattern/ "'{ nmatches++ }
END { print nmatches, "found" }' /path/to/data
The awk program consists of two pieces of quoted text that are concatenated together to form the program.
The first part is double-quoted, which allows substitution of the pattern shell variable inside the quotes.
The second part is single-quoted.
It does add the caveat though:
Variable substitution via quoting works, but can potentially be messy.
It requires a good understanding of the shell’s quoting rules (see Quoting), and it’s often difficult to correctly match up the quotes when reading the program.

Resources