bash/shell: easiest way to get kernel name components on command line - bash

On my Fedora machine I sometimes need to find out certain components of the kernel name, e.g.
VERSION=3.18.9-200.fc21
VERSION_ARCH=3.18.9-200.fc21.x86_64
SHORT_VERSION=3.18
DIST_VERSION=fc21
EXTRAVERSION = -200.fc21.x86_64
I know uname -a/-r/-m but these give me not all the components I need.
Of course I can just disassemble uname -r e.g.
KERNEL_VERSION_ARCH=$(uname -r)
KERNEL_VERSION=$(uname -r | cut -d '.' -f 1-4)
KERNEL_SHORT_VERSION=$(uname -r | cut -d '.' -f 1-2)
KERNEL_DIST_VERSION=$(uname -r | cut -d '.' -f 4)
EXTRAVERSION="-$(uname -r | cut -d '-' -f 2)"
But this seems very cumbersome and not future-safe to me.
Question: is there an elegant way (i.e. more readable and distribution aware) to get all kernel version/name components I need?
Nice would be s.th. like
kernel-ver -f "%M.%m.%p-%e.%a"
3.19.4-200.fc21.x86_64
kernel-ver -f "%M.%m"
3.19
kernel-ver -f "%d"
fc21

Of course the uname -r part would need a bit sed/awk/grep magic. But there are some other options you can try:
cat /etc/os-release
cat /etc/lsb-release
Since it's fedora you can try: cat /etc/fedora-release
lsb_release -a is also worth a try.
cat /proc/version, but that nearly the same output as uname -a
In the files /etc/*-release the format is already VARIABLE=value, so you could source the file directly and access the variables later:
$ source /etc/os-release
$ echo $ID
fedora
To sum this up a command that should work on every system that combines the above ideas:
cat /etc/*_ver* /etc/*-rel* 2>/dev/null

Related

Installed Debian package-list with version-numbers

I want to compare two Debian systems with respect to packages version numbers. For that I need a file listing of all installed packages like this:
a2ps 1:4.14-1.3
abiword 3.0.0-8+b1
acl 0.6.37-3+b1
...
I wrote a bash script (rather clumsy) that collects the required info, but I cannot make it write to a file. Can someone help me to fix this?
dpkg --get-selections \
| grep "\binstall\b" \
| sed 's/\(^[A-Za-z0-9\.\-\_]*\).*/\1/' \
| while read i ; \
do `echo $i` `apt-cache policy $i \
| grep Install \
| sed 's/ *Installed: *\([A-Za-z0-9\.\-\_]*\)/\1/' `\
; done
Thank you.
dpkg-query --show -f '${Package}\t${Version}\n' > out.txt

How to define subroutines in a Makefile

I am working on a Makefile which has a¹ receipt producing some file using M4. It uses some complex shell constructions to compute macro values which have to be passed to M4. How can I organize code to avoid redundant declarations displayed in the following example?
M4TOOL= m4
M4TOOL+= -D PACKAGE=$$(cd ${PROJECTBASEDIR} && ${MAKE} -V PACKAGE)
M4TOOL+= -D VERSION=$$(cd ${PROJECTBASEDIR} && ${MAKE} -V VERSION)
M4TOOL+= -D AUTHOR=$$(cd ${PROJECTBASEDIR} && ${MAKE} -V AUTHOR)
M4TOOL+= -D RDC960=$$(openssl rdc960 ${DISTFILE} | cut -d ' ' -f 2)
M4TOOL+= -D SHA256=$$(openssl sha256 ${DISTFILE} | cut -d ' ' -f 2)
Portfile: Portfile.m4
${M4TOOL} ${.ALLSRC} > ${.TARGET}
¹ Actually a lot!
You should define pseudo-commands using the -c option of the shell, like this:
PROJECTVARIABLE=sh -c 'cd ${PROJECTBASEDIR} && ${MAKE} -V $$1' PROJECTVARIABLE
OPENSSLHASH=sh -c 'openssl $$1 $$2 | cut -d " " -f 2' OPENSSLHASH
Note the use of $ or $$ to use bsdmake variable expansion or shell variable expansion. With these defintions you can reorganise your code like this:
M4TOOLS+= -D PACKAGE=$$(${PROJECTVARIABLE} PACKAGE)
M4TOOLS+= -D VERSION=$$(${PROJECTVARIABLE} VERSION)
M4TOOLS+= -D AUTHOR=$$(${PROJECTVARIABLE} AUTHOR)
M4TOOLS+= -D RMD160=$$(${OPENSSLHASH} rmd160 ${DISTFILE})
M4TOOLS+= -D SHA256=$$(${OPENSSLHASH} sha256 ${DISTFILE})
The result is arguably easier to read and maintain. When you write such scripts, remember to use error codes and stderr to report errors.
PS: You can take a look at the COPYTREE_SHARE macro in /usr/ports/Mk/bsd.port.mk on a FreeBSD system. It illustrates well the technique.

Bash Script to Get Operating System + Version as string

I want to make a bash script to take the system os and the version as a simple string.
Possible ways to get these info is from
/etc/issue
cat /etc/*-release
lsb_release -a
and probably some others which i dont know. The problem is that i want the bash script to work on Ubuntu 12,13,14 and CentOS. Some of the above does not work in these systems. For example the lsb_release does not work on CentOS and sometimes the /etc/issue is empty so i'm little confused about it.
As for the string i want to get it in this way (and save it to var). I will give examples.
If OS is Ubuntu 12.x i want to take it as ubuntu12
If OS is Ubuntu 13.x i want to take it as ubuntu13
If OS is CentOS 7.x i want to take it as centos7
Is that easy?
THANK YOU
Here is a bash solution. I tested on Ubuntu, but not on CentOS (I only have RHEL available now). But you can test the CentOS part and modify as needed.
#!/usr/bin/env bash
RELEASE=unknown
version=$( lsb_release -r | grep -oP "[0-9]+" | head -1 )
if lsb_release -d | grep -q "CentOS"; then
RELEASE=centos$version
elif lsb_release -d | grep -q "Ubuntu"; then
RELEASE=ubuntu$version
fi
echo $RELEASE
Or, without lsb_release on CentOS:
#!/usr/bin/env bash
RELEASE=unknown
if [ -f /etc/redhat-release ]; then
version=$( cat /etc/redhat-release | grep -oP "[0-9]+" | head -1 )
RELEASE=centos$version
elif [ -n $(which lsb_release 2> /dev/null) ] && lsb_release -d | grep -q "Ubuntu"; then
version=$( lsb_release -d | grep -oP "[0-9]+" | head -1 )
RELEASE=ubuntu$version
fi
echo $RELEASE
In any case, there's more than one way to skin this cat.

How to get the file size on Unix in a Makefile?

I would like to implement this as a Makefile task:
# step 1:
curl -u username:password -X POST \
-d '{"name": "new_file.jpg","size": 114034,"description": "Latest release","content_type": "text/plain"}' \
https://api.github.com/repos/:user/:repo/downloads
# step 2:
curl -u username:password \
-F "key=downloads/octocat/Hello-World/new_file.jpg" \
-F "acl=public-read" \
-F "success_action_status=201" \
-F "Filename=new_file.jpg" \
-F "AWSAccessKeyId=1ABCDEF..." \
-F "Policy=ewogIC..." \
-F "Signature=mwnF..." \
-F "Content-Type=image/jpeg" \
-F "file=#new_file.jpg" \
https://github.s3.amazonaws.com/
In the first part however, I need to get the file size (and content type if it's easy, not required though), so some variable:
{"name": "new_file.jpg","size": $(FILE_SIZE),"description": "Latest release","content_type": "text/plain"}
I tried this but it doesn't work (Mac 10.6.7):
$(shell du path/to/file.js | awk '{print $1}')
Any ideas how to accomplish this?
If you have GNU coreutils:
FILE_SIZE=$(stat -L -c %s $filename)
The -L tells it to follow symlinks; without it, if $filename is a symlink it will give you the size of the symlink rather than the size of the target file.
The MacOS stat equivalent appears to be:
FILE_SIZE=$(stat -L -f %z)
but I haven't been able to try it. (I've written this as a shell command, not a make command.) You may also find the -s option useful:
Display information in "shell output", suitable for initializing variables.
For reference, an alternative method is using du with -b bytes output and -s for summary only. Then cut to only keep the first element of the return string
FILE_SIZE=$(du -sb $filename | cut -f1)
This should return the same result in bytes as #Keith Thompson answer, but will also work for full directory sizes.
Extra: I usually use a macro for this.
define sizeof
$$(du -sb \
$(1) \
| cut -f1 )
endef
Which can then be called like,
$(call sizeof,$filename_or_dirname)
I think this is a case where parsing the output of ls is legitimate:
% FILE_SIZE=`ls -l $filename | awk '{print $5}'`
(no it's not: use stat, as noted by Keith Thompson)
For the type, you can use
% FILE_TYPE=`file --mime-type --brief $filename`

Strange behavior of uniq on darwin shells

I've used 'uniq -d -c file' in many shell scripts on linux machines, and it works.
On my MAC (OS X 10.6.7 with developer tools installed) it doesn't seems to work:
$ uniq -d -c testfile.txt
usage: uniq [-c | -d | -u] [-i] [-f fields] [-s chars] [input [output]]
It would be nice if anyone could checks this.
Well, it's right there in the Usage message. [ -c | -d | -u] means you can use one of those possibilities, not two.
Since OSX is based on BSD, you can check that here or, thanks to Ignacio, the more Apple-specific one here.
If you want to achieve a similar output, you could use:
do_your_thing | uniq -c | grep -v '^ *1 '
which will strip out all those coalesced lines that have a count of one.
You can try this awk solution
awk '{a[$0]++}END{for(i in a)if(a[i]>1){ print i ,a[i] } }' file

Resources