问题
After searching around for many hours testing different solutions, I've yet to find a working solution. Bash and Shell Scripting is not my strongest area.
I have a variable which has rows (new lines) and columns (tab separated). What I want to do is loop through the rows and get 'Column X' then put that item into a variable so I can do something with that Cell. i.e. echo out for example.
There are an unknown number of rows, but known number of columns, so that it is possible to always get Column 3 for example.
I'm running Bash v.4 on Linux.
Example Input (can't find a way to actually 'tab' things on here?);
UniqueID1 -tab- Description -tab- FriendlyNameABC
UniqueID1 -tab- Description -tab- FriendlyNameXYZ
Example output would be to loop through the listings, get FriendlyNameABC (col 3) and echo that out.
I am trying to expand this script here, https://www.raspberrypi.org/forums/viewtopic.php?f=63&t=77277&p=1178478#p1178478 - which is a shell script to connect to WiFi via WPS. The script provided on that page (below for reference) works, but it is reliant on selecting only the strongest WiFi signal, which isn't always going to be the network you want to connect to.
#!/bin/bash
# only run if there's no current wifi connection
if ! ifconfig wlan0 | grep -q "inet addr:" ; then
# grab AP's that support WPS, sort by strength and select the strongest
wpa_cli scan_results | grep WPS | sort -r -k3 | awk 'END{print $NF}' >/tmp/wifi
read ssid < /tmp/wifi
wpa_cli wps-pbc $ssid
fi
Currently, I've managed to modify the script above so that the line starting with 'wpa_cli..' is now reading as follows, which puts the entire identified WiFi networks in a variable, but then I'm struggling to split this all out.
wpa_cli scan_results | grep WPS | sort -r -k3 $allConnections
In essence, the above code I need translating into this pseudo code;
#!/bin/bash
# only run if there's no current wifi connection
if ! ifconfig wlan0 | grep -q "inet addr:" ; then
# grab AP's that support WPS, sort by strength and select the strongest
wpa_cli scan_results | grep WPS | sort -r -k3 | awk 'END{print $NF}' >/tmp/wifi
read ssid < /tmp/wifi
#This is the section below that I'm struggling to implement (this is pseudo code, the only bit of working code is the line starting with wpa_cli...)
while(ssid.hasNext(){
#Try connecting to the network
wpa_cli wps-pbc $ssid
if(successful){break();}
}
fi
I should add, I've assumed that the data is tab and new line delimited based on how the layout looks when I run the command;
wpa_cli scan_results
Is there a way to confirm the delimiter when running commands at the Linux command line?
回答1:
If your "for each cell" logic is simple enough, you can do something like:
echo $VARIABLE | cut -f3 -d$'\t' | xargs echo
# echo $VARIABLE | # pipe the value of VARIABLE to the next statement...
# cut -f3 -d$'\t' | # take the 3rd column, using tabs for the delimiters
# xargs echo # for each resulting row, call `echo` with that row as the argument
If you need to do something more complicated:
for CELL in $(echo $VARIABLE | cut -f3 -d$'\t'); do # for each line in the result of this command...
echo $CELL
# Other stuff with $CELL
done
回答2:
Simplicity always wins. I've managed to resolve this. The help from the people commenting, thanks as this helped me clarify a few things while testing various scripts.
After testing everything, what was clear was that the way the data was being broken out wasn't quite right. Spaces were being treated as tabs, which means that the SSID Network Name that you want to connect to, when it should be "My Network" it would simply be called "Network" which then wouldn't connect as there is no network called "Network". So after removing virtually all of the code and simplifying things, I remembered from testing previously that you don't actually need to supply a network name, you can simply run the command without any of the smarter logic, which simplifies everything.
#!/bin/bash
# only run if there's no current wifi connection
if ! ifconfig wlan0 | grep -q "inet addr:" ; then
wpa_cli wps-pbc
fi
You'll notice in the above, this script simply listens out for a WPS button being pushed and will connect to that network automatically. Regardless if this is the strongest signal, or weather this network name has a space in it or not.
回答3:
First take a moment to think about the difference between echo $a
and echo "$a"
.
In the first case the value of a
is given to echo
as seperate fields, in the second case as 1 field. So in the first case bash
will change the tabs and newlines, removing any rows and columns that might have been in $a
.
One tricky way to fill a
with 4 rows is giving printf
more parameters then required in the formatstring:
a=$(printf "%s\t%s\t%s\n" 1 a A 2 b B 3 c C 4 "F o u r" "4 4 4 4" )
After the printf
, an echo
might surprise you:
you> echo $a | sed -r 's/\t/<tab>/g'
1 a A 2 b B 3 c C 4 F o u r 4 4 4 4
you> echo "$a" | sed -r 's/\t/<tab>/g'
1<tab>a<tab>A
2<tab>b<tab>B
3<tab>c<tab>C
4<tab>F o u r<tab>4 4 4 4
You can make a loop from the calue of a, assigning each field to a different variable, with this example switching fields:
you> while IFS=$'\t' read -r id desc friend; do
echo "$id = ${friend} (${desc})"
done <<< "$a"
1 = A (a)
2 = B (b)
3 = C (c)
4 = 4 4 4 4 (F o u r)
I do not understand the line
wpa_cli scan_results | grep WPS | sort -r -k3 $allConnections
When you want to sort the grep results, $allConnections
should be removed (or empty). When the var allConnections
is filled with a filename, you can use > $allConnections
(redirect to the file), but I think you want to store the results in a var.
allConnections=$(wpa_cli scan_results | grep WPS | sort -r -k3)
And now you can do something like
while IFS=$'\t' read -r id desc friend; do
echo "Trying ssid ${id} = ${friend} (${desc})"
wpa_cli wps-pbc ${id} && break
done <<< "$allConnections"
The && break
assumes, that wpa_cli
returns 0 when it is successful.
来源:https://stackoverflow.com/questions/44722052/bash-loop-through-variable-with-rows-and-columns