Getting iwconfig output in fields - linux

I just did an iwconfig 2>/dev/null | hd and an echo $(iwconfig 2>/dev/null) | hd
Both outputs have no clear separation between fields and inner-field spaces… it's all spaces -.^
The man-page didnt bring up any way to set iwconfig's field-separator, so i read the sourcecode and there is none, all is done by concatenating sprintf's.
I've seen a script that froze hell will some awk's just to get some values and that expected the accesspoint at iwconfig | awk 'Access point:/ {print $6}' and as i had to change that for my system to $4 i wonder if gathering all infomation, stuffing it into a clumpsy output, parsing that linewise and regexing thru the lines really is the proper way to do it … is there an alternative to iwconfig that yields the same information as hash with usefull separated fields, names and values?

From the shell , awk or sed may be the only solution. But if you are writing the program in C, you can use the ioctl commands that iwconfig is actually using to print the info you see.
You might also want to give iw a try. iwconifg is being replaced by iw.

You could get the access point mac address with
iwconfig 2>&1 | sed -n -e 's/^.*Access Point: //p'

Related

Can I pipe lshw warnings to /dev/null when I run it as a standard user?

I'm trying to create an alias for getting memory on my machine, currently I have alias mem="lshw | grep size | awk -F: '{print $2}'", and when I run it as a non-super user, I get the following warning message:
WARNING: you should run this program as super-user.
WARNING: output may be incomplete or inaccurate, you should run this program as super-user.
size: 23GiB
I'm not worried about the results being potentially incomplete, in fact when I diff the output when running as root vs a standard user, it's exactly the same. Does anybody know how to get rid of these warnings? I tried piping stderr to /dev/null, but that didn't work. Does anyone else know how to get rid of these warnings?
Can I interest you in
alias mem='free -g | grep Mem | awk '\''{print $2 " GiB"}'\'
free -m will give MiB; you can change the " GiB" part to whatever you want (or remove it).
I don't have lshw installed on my machine, so I can't help you debug your version, unfortunately.
alias mem="lshw 2> /dev/null| grep size | awk -F: '{print $2}'"
Alternatively you can use free or read from /proc/meminfo
cat /proc/meminfo |grep MemTotal
I'm not sure how you piped to dev/null, but this works for me:
lshw 2> /dev/null | grep size | awk -F: '{print $2}'
Ignoring that there are other tools more suited to getting the memory, if there is something you need and lshw is your only option, you would be better suited to use -json or -xml output and use a tool to parse it like jq or xmllint. The version of lshw on my distro outputs invalid json that can't be parsed, but does have valid xml output.
This would accomplish your goal, although the path may very well be different for you:
lshw -xml 2> /dev/null | xmllint --xpath '/list/node/node/node[#id="memory"]/size/text()' -
Or add a one grep:
... | grep "size:"

Apply two greps and awk to same input

I'm using two short UNIX commands in my python script to get some data about nearby wireless access points.
n°1, gets the ESSID of the access point :
"iwlist NIC scan | grep ESSID | awk '{print $1}'"
n°2, gets the signal strength of the access point :
"iwlist NIC scan | grep level | awk '{print $3}'"
My problem is that I use these two commands one after the other which means that it doesn't generate "symmetric" data. You might get 6 ESSIDs and 4 Signal strength data.
Because the first time, the script found 6 APs (A, B, C, D, E and F) and the next time only 4 APs (A, C, E and F).
Some my question is the following :
Is there a way to "split" the result of the first iwlist NIC scan and then apply two different grep and awk sequences to the same input ?
Just so that you at least get a symmetric list of results.
Thank you in advance !
What about using awk as grep:
iwlist NIC scan | awk '/ESSID/ {print $1} /level/ {print $3}'
This gives you the ESSID and level lines all at once. You'd probably want to be a little more sophisticated and at least tag the lines with what it represents; the options are legion. It isn't clear from your code how you're going to use the output, so I'm not going to try and second-guess how best to present it (but I would expect that network ID and level on the same line would be a nice output — and it is doable).
In general, you can accomplish this type of routing using tee and process substitution:
iwlist NIC scan | tee >( grep -i ESSID | awk '{print $1}' ) | grep -i level | awk '{print $3}'
but this is inferior in this situation for several reasons:
grep is superfluous, since awk can do the filtering itself
The two branches are similar enough to fold into a single awk command, as Jonathan Leffler points out.
The two output streams are merged together in a nondeterministic manner, so it may be difficult or impossible to determine which level corresponds to which ESSID. Storing the output of each branch in a file and later matching them line by line helps, but then this is not much better than asgs's solution.
But the technique of passing one command's output to two different pipelines without an explicit temporary file may be useful elsewhere; consider this answer just a demonstration.
#!/bin/bash
iwlist <NIC> scan > tmpfile
grep -i ESSID tmpfile | awk '{print $1}'
grep -i level tmpfile | awk '{print $3}'
rm tmpfile
A script something like this might just do what you're expecting.

Get the last 4 characters of output from standard out

I have a script that is running and uses
lspci -s 0a.00.1
This returns
0a.00.1 usb controller some text device 4dc9
I want to get those last 4 characters inline such that
lspci -s 0a.00.1 | some command to give me the last 4 characters.
How about tail, with the -c switch. For example, to get the last 4 characters of "hello":
echo "hello" | tail -c 5
ello
Note that I used 5 (4+1) because a newline character is added by echo. As suggested by Brad Koch below, use echo -n to prevent the newline character from being added.
Do you really want the last four characters? It looks like you want the last "word" on the line:
awk '{ print $NF }'
This will work if the ID is 3 characters, or 5, as well.
Using sed:
lspci -s 0a.00.1 | sed 's/^.*\(.\{4\}\)$/\1/'
Output:
4dc9
Try this, say if the string is stored in the variable foo.
foo=`lspci -s 0a.00.1` # the foo value should be "0a.00.1 usb controller some text device 4dc9"
echo ${foo:(-4)} # which should output 4dc9
I usually use
echo 0a.00.1 usb controller some text device 4dc9 | rev | cut -b1-4 | rev
4dc9
If the real request is to copy the last space-separated string regardless of its length, then the best solution seems to be using ... | awk '{print $NF}' as given by #Johnsyweb. But if this is indeed about copying a fixed number of characters from the end of a string, then there is a bash-specific solution without the need to invoke any further subprocess by piping:
$ test="1234567890"; echo "${test: -4}"
7890
$
Please note that the space between colon and minus character is essential, as without it the full string will be delivered:
$ test="1234567890"; echo "${test:-4}"
1234567890
$
Try using grep:
lspci -s 0a.00.1 | grep -o ....$
This will print last 4 characters of every line.
However if you'd like to have last 4 characters of the whole output, use tail -c4 instead.
One more way to approach this is to use <<< notation:
tail -c 5 <<< '0a.00.1 usb controller some text device 4dc9'
instead of using named variables, develop the practice of using the positional parameters, like this:
set -- $( lspci -s 0a.00.1 ); # then the bash string usage:
echo ${1:(-4)} # has the advantage of allowing N PP's to be set, eg:
set -- $(ls *.txt)
echo $4 # prints the 4th txt file.

Give the mount point of a path

The following, very non-robust shell code will give the mount point of $path:
(for i in $(df|cut -c 63-99); do case $path in $i*) echo $i;; esac; done) | tail -n 1
Is there a better way to do this in shell?
Postscript
This script is really awful, but has the redeeming quality that it Works On My Systems. Note that several mount points may be prefixes of $path.
Examples
On a Linux system:
cas#txtproof:~$ path=/sys/block/hda1
cas#txtproof:~$ for i in $(df -a|cut -c 57-99); do case $path in $i*) echo $i;; esac; done| tail -1
/sys
On a Mac OSX system
cas local$ path=/dev/fd/0
cas local$ for i in $(df -a|cut -c 63-99); do case $path in $i*) echo $i;; esac; done| tail -1
/dev
Note the need to vary cut's parameters, because of the way df's output differs; using awk solves this, but even awk is non-portable, given the range of result formatting various implementations of df return.
Answer
It looks like munging tabular output is the only way within the shell, but
df -P "$path" | tail -1 | awk '{ print $NF}'
based on ghostdog74's answer, is a big improvement on what I had. Note two new issues: firstly, df $path insists that $path names an existing file, the script I had above doesn't care; secondly, there are no worries about dereferencing symlinks. This doesn't work if you have mount points with spaces in them, which occurs if one has removable media with spaces in their volume names.
It's not difficult to write Python code to do the job properly.
df takes the path as parameter, so something like this should be fairly robust;
df "$path" | tail -1 | awk '{ print $6 }'
In theory stat will tell you the device the file is on, and there should be some way of mapping the device to a mount point.
For example, on linux, this should work:
stat -c '%m' $path
Always been a fan of using formatting options of a program, as it can be more robust than manipulating output (eg if the mount point has spaces). GNU df allows the following:
df --output=target "$path" | tail -1
Unfortunately there is no option I can see to prevent the printing of a header, so the tail is still required.
i don't know what your desired output is, therefore this is a guess
#!/bin/bash
path=/home
df | awk -v path="$path" 'NR>1 && $NF~path{
print $NF
}'
Using cut with -c is not really reliable, since the output of df will be different , say a 5% can change to 10% and you will miss some characters. Since the mount point is always at the back, you can use fields and field delimiters. In the above, $NF is the last column which is the mount point.
I would take the source code to df and find out what it does besides calling stat as Douglas Leeder suggests.
Line-by-line parsing of the df output will cause problems as those lines often look like
/dev/mapper/VOLGROUP00-logical--volume
1234567 1000000 200000 90% /path/to/mountpoint
With the added complexity of parsing those kinds of lines as well, probably calling stat and finding the mountpoint is less complex.
If you want to use only df and awk to find the filesystem device/remote share or a mount point and they include spaces you can cheat by defining the field separator of awk to be a regular expression that matches the format of the numeric sizes used to display total size, used space, available space and capacity percentage. By defining those columns as the field separator you are then left with $1 representing the filesystem device/remote share and $NF representing the mount path.
Take this for example:
[root#testsystem ~] df -P
Filesystem 1024-blocks Used Available Capacity Mounted on
192.168.0.200:/NFS WITH SPACES 11695881728 11186577920 509303808 96% /mnt/MOUNT WITH SPACES
If you attempt to parse this with the quick and dirty awk '{print $1}' or awk '{print $NF}' you'll only get a portion of the filesystem/remote share path and mount path and that's no good. Now make awk use the four numeric data columns as the field separator.
[root#testsystem ~] df -P "/mnt/MOUNT WITH SPACES/path/to/file/filename.txt" | \
awk 'BEGIN {FS="[ ]*[0-9]+%?[ ]+"}; NR==2 {print $1}'
192.168.0.200:/NFS WITH SPACES
[root#testsystem ~] df -P "/mnt/MOUNT WITH SPACES/path/to/file/filename.txt" | \
awk 'BEGIN {FS="[ ]*[0-9]+%?[ ]+"}; NR==2 {print $NF}'
/mnt/MOUNT WITH SPACES
Enjoy :-)
Edit: These commands are based on RHEL/CentOS/Fedora but should work on just about any distribution.
Just had the same problem. If some mount point (or the mounted device) is sufficent as in my case You can do:
DEVNO=$(stat -c '%d' /srv/sftp/testconsumer)
MP=$(findmnt -n -f -o TARGET /dev/block/$((DEVNO/2**8)):$((DEVNO&2**8-1)))
(or split the hex DEVNO %D with /dev/block/$((0x${DEVNO:0:${#DEVNO}-2})):$((0x${DEVNO:2:2})))
Alternatively the following loop come in to my mind, out of ideas why I cannot find proper basic command..
TARGETPATH="/srv/sftp/testconsumer"
TARGETPATHTMP=$(readlink -m "$TARGETPATH")
[[ ! -d "$TARGETPATHTMP" ]] && TARGETPATHTMP=$(dirname "$TARGETPATH")
TARGETMOUNT=$(findmnt -d backward -f -n -o TARGET --target "$TARGETPATHTMP")
while [[ -z "$TARGETMOUNT" ]]
do
TARGETPATHTMP=$(dirname "$TARGETPATHTMP")
echo "$TARGETPATHTMP"
TARGETMOUNT=$(findmnt -d backward -f -n -o TARGET --target "$TARGETPATHTMP")
done
This should work always but is much more then I expect for such simple task?
(Edited to use readlink -f to allow for non existing files, -m or -e for readlink could be used instead if more components might not exists or all components must exists.)
mount | grep "^$path" | awk '{print $3}'
I missed this when I looked over prior questions: Python: Get Mount Point on Windows or Linux, which says that os.path.ismount(path) tells if path is a mount point.
My preference is for a shell solution, but this looks pretty simple.
I use this:
df -h $path | cut -f 1 -d " " | tail -1
Linux has this, which will avoid problem with spaces:
lsblk -no MOUNTPOINT ${device}
Not sure about BSD land.
f () { echo $6; }; f $(df -P "$path" | tail -n 1)

How do I select a subset of lines from the history in bash?

Quite often I grep through my bash shell history to find old commands, filepaths, etc. Having identified the history number of interest, I would like to see a few lines of context on either side, i.e. view a subset of history lines. For example:
$ history | grep ifconfig
8408 ifconfig eth0
8572 sudo ifconfig eth0 down
I would like to look at the 5 lines or so either side of line 8572. Obviously knowing the line number I can page through the history with less, but this seems very stupid. As far as I can tell, the manpage doesn't seem to have this information either.
Is there a simple way to retrieve arbitrary lines of history in bash?
grep's -C option provides context. Try:
$ history | grep -C 5 ifconfig
history | grep -C 5 ifconfig
If you only want to see it for specific line numbers, you can also use something like this
history | head -n 120 | tail -n 5
The example prints lines 116 through 120.
history | grep ifconfig -A5 -B5
A = number of lines after the match found by grep
B = number of lines before the match found by grep
You can also change the number of lines from 5 to any number you want.
type ctrl-r, then some characters (interactive searching). More information here.
I type
history | grep " 840"

Resources