Variables from makefile to bash - bash

I have next situation:
source of test.mk:
test_var := test_me
source of test.sh:
$test_var = some method that get test_var from .mk
if [ "$test_var" = "test_me" ] ; then
do something
fi
How can I get variable from .mk file to my .sh file, without grep + sed and other parsing techniques.
EDIT
I can't change .mk file

Create a makefile on the fly to load the test.mk file and print the variable:
value=$(make -f - 2>/dev/null <<\EOF
include test.mk
all:
#echo $(test_var)
EOF
)
echo "The value is $value"

Well if you can't use sed or grep, then you'll have to read the makefile database after parsing using something like:
make -pn -f test.mk > /tmp/make.db.txt 2>/dev/null
while read var assign value; do
if [[ ${var} = 'test_var' ]] && [[ ${assign} = ':=' ]]; then
test_var="$value"
break
fi
done </tmp/make.db.txt
rm -f /tmp/make.db.txt
this makes sure that something like:
value := 12345
test_var := ${value}
will output 12345, instead of ${value}
If you wanted to create variables representing all those from the makefile, you can change the inner if to:
if [[ ${assign} = ':=' ]]; then
# any variables containing . are replaced with _
eval ${var//./_}=\"$value\"
fi
so you will get variables like test_var set to the appropriate value. There are some make variables that start with ., which would need to be replaced with a shell-variable safe value like _, which is what the search-replace is doing.

Create a rule print_var in your makefile with the following code:
print_var:
echo $(test_var)
And in your test.sh, do:
$test_var = $(make print_var)
You also have to consider to put print_var rule in .PHONY section

A variation of #Idelic answer I came up some time ago on my own:
function get_make_var()
{
echo 'unique123:;#echo ${'"$1"'}' |
make -f - -f "$2" --no-print-directory unique123
}
test_var=`get_make_var test_var test.mk`
It uses the lesser known feature of the GNU make - ability to read multiple Makefiles from the command line using multiple -f options.

Related

How to check filetype in if statement bash using wildecard and -f

subjects_list=$(ls -l /Volumes/Backup_Plus/PPMI_10 | awk '{ print $NF }')
filepath="/Volumes/Backup_Plus/PPMI_10/$subjects/*/*/S*/"
for subjects in $subjects_list; do
if [[ -f "${filepath}/*.bval" && -f "${filepath}/*.bvec" && -f "${filepath}/*.json" && -f "${filepath}/*.nii.gz" ]]; then
echo "${subjects}" >> /Volumes/Backup_Plus/PPMI_10/keep_subjects.txt
else
echo "${subjects}" >> /Volumes/Backup_Plus/PPMI_10/not_keep_subjects.txt
fi
done
problem is supposedly in the if statement, I tried this...
bvalfile = (*.bval)
bvecfile =(*.bvec)
jsonfile =(*.json)
niigzfile =(*.nii.gz)
if [[ -f "$bvalfile" && -f "$bvecfile" && -f "$jsonfile" && -f "$niigzfile" ]]; then
however that didn't work. Any help with syntax or errors or does it need to be changed completely. Trying to separate the files that have .^file types from those that don't by making two lists.
thanks
You're assigning filepath outside the for-subject loop but using the unset variable $subjects in it. You want to move that inside the loop.
Double-quoted wildcards aren't expanded, so both $filepath and your -f test will be looking for filenames with literal asterisks in them.
-f only works on a single file, so even if you fix the quotes, you'll have a syntax error if there's more than one file matching the pattern.
So I think what you want is something like this:
# note: array assignment -
# shell does the wildcard expansion, no ls required
prefix_list=( /Volumes/Backup_Plus/PPMI_10/* )
# and array expansion
for prefix in "${prefix_list[#]}"; do
# the subject is just the last component of the path
subject=${prefix##*/}
# start by assuming we're keeping this one
decision=keep
# in case filepath pattern matches more than one directory, loop over them
for filepath in "$prefix"/*/*/S*/; do
# if any of the files don't exist, switch to not keeping it
for file in "$filepath"/{*.bval,*.bvec,*.json,*.nii.gz}; do
if [[ ! -f "$file" ]]; then
decision=not_keep
# we have our answer and can stop looping now
break 2
fi
done
done
# now append to the correct list
printf '%s\n' "$subject" >>"/Volumes/Backup_Plus/PPMI_10/${decision}_subjects.txt"
done

Bash run command according to environment variable

See I wanna copy a file to a destination: cp filename /home/example/temp.txt.
The question the filename will be changed by some programes, and the new name of it will be written in file /home/example/.env.
What I want is alias something like alias cpf=cp ${filename} /home/nope/temp.txt to .bashrc, then what I need is only run cpf if I want to copy the latest finename to /home/example/temp.txt.
What I have tried:
eval $(grep -v "^#" "/home/example/.env") cp ${filename} /home/nope/temp.txt
and faild to get ${filename}.
Is there some changes to make what I tried work?
Example .env:
key1='do not put me in the environment'
key2=1231
filename=thisvaluechanges
key4="I hate being evaluated"
You only want to evaluate the line with filename. First test how you can select that line, something like
grep "^filename=" /home/example/.env
# or
sed -n 's/^\s*filename\s*=\s*/filename=/p' /home/example/.env
Next you can source the selected line.
source <(grep "^filename=" /home/example/.env)
When the filename is a fixed string (without $() that needs to be evaluated), you can do without source:
cp $(grep "^filename=" /home/example/.env) /home/nope/temp.txt
Before putting this in an alias, remember that a function can do everything an alias can, and can do more. You "should" stop using alias.
When you have three or four files like filename1, 2, 3, 4, you can use a function with an argument:
cpf() {
if (( $# = 0 )); then
echo "Usage: cpf filenumber"
else
cp $(grep "^filename${1}=" /home/example/.env) /home/nope/temp.txt
fi
}
You can call the function with cpf 2 for filename2.
When you want to put the filename in the environment, you can change the function
source <(grep "^filename${1}=" /home/example/.env)
My guess is that assuming /home/example/.env contains:
#!/bin/bash
# bash sourcable file
filename=$(echo 123)
then you want:
#!/bin/bash
cpf() {
(
. /home/example/.env
cp "$filename" /home/nope/temp.txt
)
}
Notes:
eval is evil. Your use of eval $(grep...) is very dangerous.
Always remember to qoute your expansions.

How to import a config file variable, but use a different separator?

First, source and . are not working as I'm using a different kind of separator, which is something like.
I have tried several methods I can google, but didn't have any luck so far.
I managed to print out all the variables and values correctly, but I can't store it as a variable in this bash process.
What I want:
At end of the bash process when I "echo $HUA_IP:"
it should give me "192.168.0.1" as per the config.cf file.
File config.cf:
"HUA_PASSWORD": "admin",
"HUA_IP": "192.168.0.1"
While my bash file is:
#!/bin/bash
configFile="/opt/config.cf"
# config="`cat $configFile`"
# echo $config
# source $configFile
# echo $var1
# conf="";
while read var value
do
# export "$var"="$value"
var="${var%:*}"
var="${var//\"/}"
var="${var//[\}\{]/}"
value="${value//\"/}"
value="${value//,/}"
# echo "var :'"$var"'"
# echo "value :'"$value"'"
if [ !$var = "" ]
then
# "$var"="$value"
# eval $var=$value
export "$var"="$value"
fi
done < $configFile
echo $HUA_IP:
Try:
while read -r line; do
line=${line//\"/}
declare -x "${line/: /=}"
done<config.cf
echo "$HUA_IP"
When this code is run, the output is:
192.168.0.1
How it works
The key here is that declare, which is a bash builtin, allows you to use a bash variable to create and assign another variable. As a simple example:
$ x="a=b"; declare -x "$x"; echo "$a"
b
Now, let's apply this to your input file:
while read -r line; do
This starts a loop reading one line of input at a time.
line=${line//\"/}
This removes all double-quotes from the input line.
declare -x "${line/: /=}"
This replaces : with = in line and then creates a variable using declare.
The -x option tells bash to export the variable that is declared.
done <config.cf
This tells the loop to get its stdin from config.cf.
Your code, even if it could be not the best approach, is working if you change your if condition. The correct way is:
#...
if [ ! -z $var ]
then
# "$var"="$value"
# eval $var=$value
export "$var"="$value"
fi

Parse ${} placeholder into absolute path in shell script

I have a app.properties file something like below
Base.dir="/user/test/application"
Result.dir="${base.dir}/result"
and i've create bash script to parse above properties
function readConfigFile()
{
(grep -E "^${2}=" -m 1 "${1}" 2>/dev/null || echo "VAR=__UNDEFINED__") | head -n 1 | cut -d '=' -f 2-;
}
function setConfigFile()
{
sourceFile=${1}
}
function configGet()
{
if [ ! -z $sourceFile ]; then
val="$(readConfigFile $sourceFile "${1}")";
if [ "${val}" = "__UNDEFINED__" ]; then
echo "${1} value not exist"
# return empty string
printf -- "%s" "";
fi
printf -- "%s" "${val}";
else
echo "config file not exist"
# return empty string
printf -- "%s" "";
fi
}
and the way i call above parser is something like below
$Result_dir=$(configGet Result.dir)
however, i cant really translate placeholder ${} into base_dir
and i got following error
ls $Result_dir
ls: cannot access ${Base_dir}/result: No such file or directory
Is there any way that i can translate ${Base.dir} into /user/test/application?
I guess you're not going to be able to substitute ${base.dir} (btw shouldn't it be ${Base.dir}?) the way you were hoping mainly because, as far as I know, dots are not allowed in variable names in bash.
What you could do is manually substitute the ${base.dir} part with the corresponding path using bash's substitution syntax. For example:
setConfigFile 'app.properties'
Result_dir_raw=$(configGet Result.dir)
Result_dir=${Result_dir_raw/'${base.dir}'/$(configGet Base.dir)}
echo ${Result_dir}
I say "manually" because you still specify in your source code that the pattern you want to replace is ${base.dir} which I'm guessing isn't what you wanted.
Now if you run this you'll see that the ${Result_dir} variable evaluates to ""/user/test/application"/result" which obviously isn't a path, and this is because you're surrounding the paths in app.properties with double quotes, so you either need to get rid of them in your readConfigFile function or lose them altogether in your config file, which to me makes more sense.
Why have you a . in your variable name, which is not allowed in bash:
$ Base.dir="/user/test/application"
-bash: Base.dir=/user/test/application: No such file or directory
$ Base_dir="/user/test/application"
$
So, why do you get No such file or directory? Here is an explanation:
Create a file called Base.dir=gash.sh, yes, that's a legal filename
$ echo 'echo Hello World' > Base.dir=gash.sh
Make the file executable:
$ PATH=$PATH:.
$ chmod u+x Base.dir=gash.sh
Now type the command:
$ Base.dir="gash.sh"
Hello World
Use an underscore, not a dot. By the Way, ksh Korn shell not only allows the dot, it has a special meaning, it is a compound variable.

Variable with '-' (minus signals) in Bash

This is so simple yet...
FOLDER='/home/user/.ssh'
SSH="$FOLDER/local-rsync-key.pub"
if [ -f "$SSH" ]; then
...
It looks that Bash considers the '-' as a minus signal and the IF statement always fails... How can I write this variable the right way?
UPDATE:
This is another real example:
I am tring to rename files with "-" in front of the filename, for example: "-0001.jpg"
However, everyime I try to run:
for i in *; do mv "$i" "${i//-/}"; done
or:
for i in *; do mv "$i" "${i#*-}"; done
I got this error:
mv: invalid option -- '0'
Try `mv --help' for more information.
Thanks for any light!
You should not have a $ in front of your SSH assignment, that's only needed when you're using the variable. Without that, it works fine, as in the following transcript:
pax> touch abc-xyz
pax> ll a*
-rw-r--r-- 1 pax paxgrp 0 2011-06-24 05:15 abc-xyz
pax> FOLDER=.
pax> $SSH="$FOLDER/abc-xyz"
bash: =./abc-xyz: No such file or directory
pax> SSH="$FOLDER/abc-xyz"
pax> if [ -f "$SSH" ]
...> then
...> echo yes
...> fi
yes
pax> _
The answer is to use "--" (indicating no more options) after "mv" or "./" before the name of the file (indicating it is about a file). For example:
for i in *; do mv -- "$i" "${i#*-}"; done
or:
for i in *; do mv -- "$i" "./${i#*-}"; done
In bash syntax, when you set a variable just use the variable name:
VAR=value
When you reference the variable, use the $ prefix:
echo $VAR
Your code has a stray dollar sign prefix where you are trying to set the SSH variable. The dashes inside the variable should be no problem.

Resources