Using shell in Makefile to find Ubuntu Version - shell

I'm trying make Ubuntu version specific distros of my tool so I want to get the os name and version. I have the following code:
ifeq ($(OS),Windows_NT)
OS_POD+=win
else
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Linux)
OS_VERS := $(shell lsb_release -a 2>/dev/null | grep Description | awk '{ print $2 "-" $3 }')
OS_POD=./dist/linux/$(OS_VERS)
endif
ifeq ($(UNAME_S),Darwin)
OS_POD=./dist/mac
endif
endif
I use the shell one-liner:
lsb_release -a 2>/dev/null | grep Description | awk '{ print $2 "-" $3 }'
Which properly returns Ubuntu-12.04.2 outside of the Makefile, but inside it it returns nothing. That is, the OS_VERS variable is just -.
How can I fix this?

In a Makefile, $ is special. Use $$ where you want the shell to find a dollar.
OS_VERS := $(shell lsb_release -a 2>/dev/null | grep Description | awk '{ print $$2 "-" $$3 }')

You need to escape the $ inside your command.
OS_VERS:=$(shell lsb_release -a 2>/dev/null | grep Description | awk '{ print $$2 "-" $$3 }')
The following sample Makefile prints correctly so it may be a different part of your Makefile.
print:
#echo $(OS_VERS)
OS_VERS:=$(shell lsb_release -a 2>/dev/null | grep Description | awk '{ print $$2 "-" $$3 }')

OS_VERS := $(shell cat /etc/os-release | grep ^NAME | cut -d'=' -f2 | sed 's/\"//gI')

Related

One liner working, but in bash script not working, why?

oneliner
curl "127.0.0.1:81/webadmin/script?command=|ps%20-T%20-f" | grep oscam | awk 'BEGIN{IGNORECASE=1;oscam;RS="<br>"}; {print $11};' | awk '{print "/file?file="$0"/oscam.server"}' | awk '!x[$0]++'
and bash style
#!/bin/sh
OSCAM="/webadmin/script?command=|ps%20-T%20-f" | grep oscam | awk 'BEGIN{IGNORECASE=1;oscam;RS="<br>"}; {print $11};' | awk '{print "/file?file="$0"/oscam.server"}' | awk '!x[$0]++' > oscam.source.tmp
URL2=$(cat oscam.source.tmp)
for URL in `cat links.md`; do echo $URL; curl -m 5 $1 "$URL$OSCAM" > oscam.source; curl -m 5 $1 "$URL$URL2"
done > oscam.server.new
the main problem for me on script didnt running normally, didnt gave an output for oscam.source.tmp
ok refined the script
now finally working :),
#!/bin/bash
for URL in $(< links.md); do echo curl -L -m 5 $1 "'"$URL"/webadmin/script?command=|find%20/etc%20/var%20/usr%20|%20egrep%20%22CCcam.cfg|oscam.server%22'" | bash - | egrep "oscam.server<br>|CCcam.cfg" | awk 'BEGIN{RS="<br>"} {print $1}' > oscam.source.bak && awk '!/^$/' oscam.source.bak | awk '$0="/file?file="$0' > oscam.temp;
for URL2 in $(< oscam.temp); do echo curl -L -m 5 $1 "$URL$URL2" | bash -
done
done > oscam.server.new

Makefile findstring won't match numerical character

CLANG_MAJOR_VER = x`${CXX} --version | egrep -i 'clang' | awk '{print $$3}' | awk '{ gsub(/\./, " "); print $$1 }'`xx
PAT = x6
CLANG_NAME = `${CXX} --version | grep -i 'clang' | awk '{print $$1}'`
CLANG_DEF = $(if $(findstring $(PAT),$(CLANG_MAJOR_VER)),abc,def)
all:
#echo $(CLANG_MAJOR_VER)
#echo $(CLANG_NAME)
#echo $(CLANG_DEF)
Result :
x6xx
clang
and if PAT is x or xx, then $(CLANG_DEF) will be abc, if PAT = 6, x6, 6x then $(CLANG_DEF) is def.
Your use of backticks would be fine in a shell script but it is not in a Makefile. The recursive assignments (VAR = VALUE) assign the exact right-hand-side strings to your variables and make expands them recursively when they are needed and only then, that is, in your case, when make expands the recipe of your all rule, just before passing it to the shell. So, if PAT = x6, when CLANG_DEF is expanded you have (decomposed in successive steps):
#echo $(CLANG_DEF)
#echo $(if $(findstring $(PAT),$(CLANG_MAJOR_VER)),abc,def)
#echo $(if $(findstring x6,x`${CXX} --version | egrep -i 'clang' | awk '{print $$3}' | awk '{ gsub(/\./, " "); print $$1 }'`xx),abc,def)
#echo $(if $(findstring x6,x`clang --version | egrep -i 'clang' | awk '{print $3}' | awk '{ gsub(/\./, " "); print $1 }'`xx),abc,def)
#echo $(if ,abc,def)
#echo def
And this does not depend on the result of the evaluation by the shell of:
clang --version | egrep -i 'clang' | awk '{print $3}' | awk '{ gsub(/\./, " "); print $1 }'
because it is not evaluated by the shell, it is only considered as a string by make when expanding $(findstring...)...
But if PAT = x, the recursive expansion becomes:
#echo $(CLANG_DEF)
#echo $(if $(findstring $(PAT),$(CLANG_MAJOR_VER)),abc,def)
#echo $(if $(findstring x,x`${CXX} --version | egrep -i 'clang' | awk '{print $$3}' | awk '{ gsub(/\./, " "); print $$1 }'`xx),abc,def)
#echo $(if $(findstring x,x`clang --version | egrep -i 'clang' | awk '{print $3}' | awk '{ gsub(/\./, " "); print $1 }'`xx),abc,def)
#echo $(if x,abc,def)
#echo abc
Instead of this unusual use of backsticks you could try to use the $(shell...) make function:
CLANG_MAJOR_VER = x$(shell ${CXX} --version | egrep -i 'clang' | awk '{print $$3}' | awk '{ gsub(/\./, " "); print $$1 }')xx
PAT = x6
CLANG_NAME = $(shell ${CXX} --version | grep -i 'clang' | awk '{print $$1}')
CLANG_DEF = $(if $(findstring $(PAT),$(CLANG_MAJOR_VER)),abc,def)
all:
#echo $(CLANG_MAJOR_VER)
#echo $(CLANG_NAME)
#echo $(CLANG_DEF)
And, by the way, your way of retrieving the version major number is not very robust (indeed, it fails on my Mac OS). Something like:
clang --version | sed -n '/version/s/.*version \([0-9]\+\).*/\1/p'
may be a bit better (not %100 sure, tested only on two OSes and two clang versions).
There is a library which can dissect simple (glob) string expressions:
include gmtt/gmtt.mk
define clang--version =
clang version 3.18.240-2ubuntu4 (tags/RELEASE_380/final)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
endef
parsed-string = $(call glob-match,$(clang--version),clang version *.*.*-* *Target: *Thread model: *InstalledDir: *)
$(info $(parsed-string))
$(info Major: $(word 2,$(parsed-string)) Minor: $(word 4,$(parsed-string)) Bugfix: $(word 6,$(parsed-string)))
Output:
clang§version§ 3 . 18 . 240 - 2ubuntu4 § (tags/RELEASE_380/final) Target:§ x86_64-pc-linux-gnu Thread§model:§ posix InstalledDir:§ /usr/bin
Major: 3 Minor: 18 Bugfix: 240

awk command has different behaviors when executing the exact same code. Why?

I have created a little shellscript that is capable of receiving a list of values such as "MY_VAR_NAME=var_value MY_VAR_NAME2=value2 ...", separated by spaces only. There should be also the possibility to use values such as MY_VAR_NAME='' or MY_VAR_NAME= (nothing).
These values are then used to change the value inside a environment variables file, for example, MY_VAR_NAME=var_value would make the script change the MY_VAR_NAME value inside the .env file to var_value, without changing anything else about the file.
The env file has the following configuration:
NODE_ENV=development
APP_PATH=/media
BASE_URL=http://localhost:3000
ASSETS_PATH=http://localhost:3000
USE_CDN=false
APP_PORT=3000
WEBPACK_PORT=8080
IS_CONNECTED_TO_BACKEND=false
SHOULD_BUILD=false
USE_REDUX_TOOL=false
USE_LOG_OUTPUT_AS_JSON=false
ACCESS_KEY_ID=
SECRET_ACCESS_KEY=
BUCKET_NAME=
BASE_PATH=
MIX_PANEL_KEY=
RDSTATION_KEY=
RESOURCE_KEY=
SHOULD_ENABLE_INTERCOM=false
SHOULD_ENABLE_GTM=false
SHOULD_ENABLE_UTA=false
SHOULD_ENABLE_WOOTRIC=false
I have debugged my script, and found out that this is the point where sometimes it has a problem
cat .envtemp | awk -v var_value="$VAR_VALUE" \
-v var_name="$VAR_NAME" \
-F '=' '$0 !~ var_name {print $0} $0 ~ var_name {print $1"="var_value}' | tee .envtemp
This piece of code sometimes outputs to .envtemp the proper result, while sometimes it just outputs nothing, making .envtemp empty
The complete code i am using is the following:
function change_value(){
VAR_NAME=$1
VAR_VALUE=$2
cat .envtemp | awk -v var_value="$VAR_VALUE" \
-v var_name="$VAR_NAME" \
-F '=' '$0 !~ var_name {print $0} $0 ~ var_name {print $1"="var_value}' | tee .envtemp
ls -l -a .env*
}
function manage_env(){
for VAR in $#
do
var_name=`echo $VAR | awk -F '=' '{print $1}'`
var_value=`echo $VAR | awk -F '=' '{print $2}'`
change_value $var_name $var_value
done
}
function main(){
manage_env $#
cat .envtemp > .env
exit 0
}
main $#
Here is an example script for recreating the error. It does not happen every time, and when it happens, it is not always with the same input.
#!/bin/bash
ENV_MANAGER_INPUT="NODE_ENV=production BASE_URL=http://qa.arquivei.com.br ASSETS_PATH=https://d4m6agb781hapn.cloudfront.net USE_CDN=true WEBPACK_PORT= IS_CONNECTED_TO_BACKEND=true ACCESS_KEY_ID= SECRET_ACCESS_KEY= BUCKET_NAME=frontend-assets-dev BASE_PATH=qa"
cp .env.dist .env
#Removes comment lines. The script needs a .envtemp file.
cat .env.dist | grep -v '#' | grep -v '^$' > .envtemp
./jenkins_env_manager.sh ${ENV_MANAGER_INPUT}
Have you tried use two files:
mv .envtemp .envtemp.tmp
cat .envtemp.tmp | awk ... | tee .envtemp

Bash output to multiple variales

I'm creating a script in Bash to change all MAC addresses of my PC. I can list all network interfaces with this:
ip link | grep "<" | cut -d " " -f 2 | cut -d ":" -f 1 | grep -v lo
And the output of the script is:
eth0
wlan0
Now I need to create a variable for each network interface (to use it in the future), but I don't know how, and Google didn't help me...
Answer:
readarray -t interfaces < <(ip link | grep "<" | cut -d " " -f 2 | cut -d ":" -f 1 | grep -v lo)
echo "${interfaces[0]}" # prints eth0
echo "${interfaces[1]}" # prints wlan0
And to loop over them use for:
for curInterface in "${interfaces[#]}"; do
echo "$curInterface"
done
But there are better ways to parse data:
First of all, instead of grepping < character you can use -o flag. This will output all of the data on single lines. Then you simply need the second word without : character. This is very simple in pure bash:
interfaces=()
while read -r _ curInterface _; do
interfaces+=("${curInterface%:}")
done < <(ip -o link)
Store the output in an array:
interfaces=( $(ip link | awk '/</ { print $2 }' | awk -F: '!/lo/ {print $1}') )
You can create an array from this output, and loop through it after.
my_array=( $(ip link | grep "<" | cut -d " " -f 2 | cut -d ":" -f 1 | grep -v lo) )
You can also this exmaple giving different alternatives redirect output to array
And I could have it simpler like this with one awk command:
readarray -t youravar < <(exec ip link | awk -F': ' '/^[0-9]+:/&&!/ lo: /{print $2}')

Bash: "xargs cat", adding newlines after each file

I'm using a few commands to cat a few files, like this:
cat somefile | grep example | awk -F '"' '{ print $2 }' | xargs cat
It nearly works, but my issue is that I'd like to add a newline after each file.
Can this be done in a one liner?
(surely I can create a new script or a function that does cat and then echo -n but I was wondering if this could be solved in another way)
cat somefile | grep example | awk -F '"' '{ print $2 }' | while read file; do cat $file; echo ""; done
Using GNU Parallel http://www.gnu.org/software/parallel/ it may be even faster (depending on your system):
cat somefile | grep example | awk -F '"' '{ print $2 }' | parallel "cat {}; echo"
awk -F '"' '/example/{ system("cat " $2 };printf "\n"}' somefile

Resources