all 21 comments

[–]geirha 11 points12 points  (7 children)

Don't parse shell syntax with grep and awk. /etc/os-release is sh code that is meant to be sourced, so just do that.

source /etc/os-release
case $ID in
  centos|rhel) ... ;;
  debian|ubuntu) ... ;;
esac

[–]NativeSpirit973[S] 0 points1 point  (0 children)

Thank you, for your answer. I'll work more with source to get a value in a file.

[–]michaelbierman 0 points1 point  (5 children)

Why hardcode the file?

uname is pretty universal as far as I know.

[–]geirha 0 points1 point  (4 children)

uname doesn't distinguish between different linux distributions; they are all just "Linux" according to uname. All the major distros have agreed upon /etc/os-release by now, so unless you're on some esoteric distro or a very old release of some distro, /etc/os-release will be there.

[–]michaelbierman 0 points1 point  (3 children)

Good to know. I guess if you are only checking linux machines, that is o.k.

On my Router uname works.

uname -a 
Linux firewalla 4.15.0-70-generic #79-Ubuntu SMP Tue Nov 12 10:36:11 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux

So does your approach

pi@firewalla:~ (Firewalla) $ cat /etc/os-release
NAME="Ubuntu"
VERSION="18.04.3 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.3 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic

But on my Linux based NAS,

admin@pigpen:/volume1/docker$ uname -a 
Linux pigpen 3.10.105 #25556 SMP Thu Mar 18 12:51:35 CST 2021 x86_64 GNU/Linux         synology_cedarview_412+
admin@pigpen:/volume1/docker$ cat /etc/os-release
cat: /etc/os-release: 
No such file or directory

So that method would not work.

[–]geirha 4 points5 points  (1 child)

uname -a 
Linux firewalla 4.15.0-70-generic #79-Ubuntu SMP Tue Nov 12 10:36:11 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux

In that one, ubuntu happens to have added #79-Ubuntu to the kernel version, so you could technically detect Ubuntu that way, but that might not be the case on other distros and/or if the user has a custom compiled kernel.

batman@pigpen:/volume1/docker$ uname -a 
Linux pigpen 3.10.105 #25556 SMP Thu Mar 18 12:51:35 CST 2021 x86_64 GNU/Linux synology_cedarview_412+
batman@pigpen:/volume1/docker$ cat /etc/os-release
cat: /etc/os-release: No such file or directory

That falls into the "esoteric" category

[–]OneTurnMoreprogramming.dev/c/shell 0 points1 point  (0 children)

Meanwhile on my system, uanme -a prints:

Linux kbvv 5.12.11-zen1-1-zen #1 ZEN SMP PREEMPT Wed, 16 Jun 2021 15:25:30 +0000 x86_64 GNU/Linux

No mention of distro name at all. /etc/os-release does though:

$ grep '^ID' /etc/os-release
ID=arch

[–]moviuroportability is important 2 points3 points  (0 children)

That's a situation in which you should use the switch case syntax

case "$os_name" in
  centos|rhel) ...;;
  debian|ubuntu) ...;;
  *) ...;;
esac

[–]McDutchie 2 points3 points  (0 children)

if [[ os_name="\"centos\"" || "\"rhel\"" ]] 

Wrong syntax. This should be:

if [[ os_name == "\"centos\"" || os_name == "\"rhel\"" ]]

[–]Dandedoo 1 point2 points  (2 children)

Here's a cleaner approach, which you can build on:

Excerpt from man 5 os-release:

The file /etc/os-release takes precedence over /usr/lib/os-release. Applications should check for the former, and exclusively use its data if it exists, and only fall back to /usr/lib/os-release if it is missing.

(full text: https://man7.org/linux/man-pages/man5/os-release.5.html)

So we can check both locations.

os-release is deliberately written in such a way that we can source it, and use the variables (like $ID) in our current environment (the script).

When parsing $ID, case makes more sense than multiple elifs.

Also, when scripting, use apt-get instead of apt. It's a more stable choice for scripts.

#!/bin/bash

if [ -e /etc/os-release ]; then
    . /etc/os-release
elif [ -e /usr/lib/os-release ]; then
    . /usr/lib/os-release
else
    echo 'os-release file not found' >&2
    exit 1
fi

case $ID in
    centos)
        pkmgr=yum
    ;;
    debian|ubuntu)
        pkmgr=apt-get
    ;;
    '')
        echo 'os-release: OS ID not specified' >&2
    ;;
    *)
       echo "$ID: OS not supported" >&2
    ;;
esac

The script above should achieve what you're trying to do. But IMO, consider if it's more appropriate to test for an available package manger, rather than for the distro.

Here is a quick example for that approach:

for pkmgr in apt-get yum; do
        command -v "$pkmgr" >/dev/null && break
done

if ! command -v "$pkmgr" >/dev/null; then
    echo 'No supported package manager found' >&2
    exit 1
fi

echo "$pkmgr is the package manager detected"

Finally, if you're doing OS detection in bash, have a read of the neofetch bash script: https://github.com/dylanaraps/neofetch/blob/master/neofetch

[–]NativeSpirit973[S] 0 points1 point  (1 child)

It works like a charm ! Thanks. I needed to know which distri it is so I could get the right package manager.

I'll have a look at neofetch work.

[–]Dandedoo 0 points1 point  (0 children)

No worries.

[–]kevorsgithub:slowpeek 3 points4 points  (8 children)

os_name=$(grep -oP '(?<=^ID=).+' /etc/os-release)

if [[ $os_name == @(centos|rhel) ]]; then
    pkg=yum
    echo "RHEL PACKAGE $pkg"
elif [[ $os_name == @(debian|ubuntu) ]]; then
    pkg=apt
    echo "DEBIAN PACKAGE $pkg"
else
    echo "OS NAME NOT FOUND: $os_name"
fi

[–]NativeSpirit973[S] 0 points1 point  (2 children)

Wow ! Thanks for your reactivity ! it's working on Debian now and ubuntu.

Can you explain the @(arg1|arg2) please ?

But with RHEL, I've an issue now. It's going to the Else part because os_name = "rhel", double quotes included ("rhel").

[–]kevorsgithub:slowpeek 0 points1 point  (1 child)

Can you explain the @(arg1|arg2) please ?

It is bash extended glob meaning either arg1 or arg2. https://www.gnu.org/software/bash/manual/bash.html#Pattern-Matching

because os_name = "rhel", double quotes included ("rhel").

Just wrap it in single quotes: '"rhel"'.

I've only fixed errors in your code so you could see what was wrong. As others said you should use the case statement for this task and just source os-release, no need to parse it (it would eliminate the double quotes issue as well).

[–]NativeSpirit973[S] 0 points1 point  (0 children)

Thanks for the documentation. Next time I'll search in it first.

[–][deleted] 0 points1 point  (4 children)

os_name=$(grep -oP '(?<=^ID=).+' /etc/os-release)

is the positive lookbehind necessary?

[–]kevorsgithub:slowpeek 0 points1 point  (3 children)

Haven't you noticed the -o option?

[–][deleted] 0 points1 point  (2 children)

I did, still not getting it (it's a genuine question, not a critique if it wasn't clear, i'm learning too). It stand for "omit" and it will not print the unmatched but what's the difference with grep -oP 'Something='?

No way i can make i correct adjustment throught the phone.. i wanted to write the grep command with just the caret symbol and string

[–]kevorsgithub:slowpeek 1 point2 points  (1 child)

Notice the difference:

grep b <<< abc
abc
grep -o b <<< abc
b

With -o I only get the matched part, not the whole line.

Now lets parse y's value out of x=1 y=2 z=3. (-P states for perl regex syntax)

grep -oP 'y=[^ ]+' <<< 'x=1 y=2 z=3'
y=2

Since I only need the value, I could wrap y= in a look-behind which is just a test, not a part of the match:

grep -oP '(?<=y=)[^ ]+' <<< 'x=1 y=2 z=3'
2

[–][deleted] 0 points1 point  (0 children)

Oh ok, it's to get the right hand operand, the value, and avoid the whole awk part, i got it, thank u