How to define local variable in Buildroot which is an execution result of bash script - linux

There's a local variable in netmap/LINUX/configure named BUILDDIR and its value is BUILDDIR=$PWD.It should have to be redirected to $(#D) which is netmap package build directory, /usr/local/buildroot/output/build/netmap-master in my case;otherwise, object files will be outputed to buildroot root directory.
I created a variable named NETMAP_CURRENT_BUILD and let it be /usr/local/buildroot/output/build/netmap-master,$(#D),
and then I wanna replace BUILDDIR=$PWD to
BUILDDIR=/usr/local/buildroot/output/build/netmap-master. By using sample code as following, it can't be done.
Sample Code :(sed part worked fine at terminal console)
define NETMAP_BUILD_CMDS
NETMAP_CURRENT_DIR = $(sed -e 's/\//\\\//g' <<< "$(#D)") -- empty
echo "$$(sed -e 's/\//\\\//g' <<< "$(#D)")" -- this line works fine
...
sed -e 's/BUILDDIR=$$PWD/BUILDDIR=$(NETMAP_CURRENT_DIR)/g' -i $(#D)/LINUX/configure
(double $$PWD, it has to be like this, which means string $PWD is needed rather than its value.)
...
endef

You don't actually need this, see below. However, if you do need the result of a shell command in a make variable, use $(shell ...)
Since this is a makefile, the $ are interpreted by make, not by the shell. Therefore, make will try to evaluate sed -e 's/\//\\\//g' <<< "$(#D)" as a variable name. There is of course no variable with that name, so you get an empty string.
To let make run a shell command and store the result in a make variable, use the $(shell ...) function. So
NETMAP_CURRENT_DIR = $(shell sed -e 's/\//\\\//g' <<< "$(#D)")
Note that that should be done outside of the NETMAP_BUILD_CMDS, because the contents of NETMAP_BUILD_CMDS is in fact a shell script: make first expands all variables in that script, then passes it to the shell line-by-line. The shell will actually error out on the above statement because you have spaces around the = so NETMAP_CURRENT_DIR is interpreted as a command name.
If you want to store the result in a shell variable rather than a make variable, then the easiest solution is to use backticks `...` to delimit the command instead of $(...):
define NETMAP_BUILD_CMDS
NETMAP_CURRENT_DIR=`sed -e 's/\//\\\//g' <<< "$(#D)"`; \
sed -e 's/BUILDDIR=$$PWD/BUILDDIR=$$NETMAP_CURRENT_DIR/g' -i $(#D)/LINUX/configure
endef
However, in your case you don't need this at all.
First of all, you can do simple substitutions directly in make.
NETMAP_CURRENT_DIR = $(subst /,\/,$(#D))
Even better, you can simply use a different delimiter than / in your sed expression, then there is no need to quote it:
sed -e 's#BUILDDIR=$$PWD#BUILDDIR=$(#D)#g' -i $(#D)/LINUX/configure
One final note: modifications to the code like this are better done in NETMAP_POST_PATCH_HOOKS than in NETMAP_BUILD_CMDS. So finally, the best solution is:
define NETMAP_FIX_BUILDDIR
sed -e 's#BUILDDIR=$$PWD#BUILDDIR=$(#D)#g' -i $(#D)/LINUX/configure
endef
NETMAP_POST_PATCH_HOOKS += NETMAP_FIX_BUILDDIR

Related

sed variable substitution using string

How to pass argument to sed without input files. The sed is in single quote and has pipe in it already.
git filter-branch -f --index-filter \
'git ls-files -s | sed -i "s-\t\"*-&dirname/-" |
GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
git update-index --index-info &&
mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEAD
The above code works fine if dirname is hard-coded. But i need to pass the dirname from command line arguments to script. I am trying like below but no luck.
'set subdir=$folder;git ls-files -s | sed -i "s-\t\"*-&$subdir/-" |
The sed says input file is missing. I can not directly pass the input file to sed. I am seeing error like below.
Rewrite 9c86de42b7f3228e3d45a278c8caf7e24c8e55cf (1/2) (0 seconds passed, remaining 0 predicted)
sed: no input files
You have two choices here. You can either evaluate $subdir to build up a fixed sed command that you then set as your filter; or you can evaluate a variable in the shell fragment that git filter-branch will invoke.
To understand the latter, realize that your --index-filter string becomes an ordinary shell variable:
--index-filter)
filter_index="$OPTARG"
;;
which is then passed to eval:
eval "$filter_index" < /dev/null ||
die "index filter failed: $filter_index"
The eval means that the expression in $filter_index, set from your --index-filter argument, has access to all the shell variables and functions in the filter-branch script. Unfortunately, none of its private variables holds the expression you'd likeā€”but you can access its environment variables, which means you can put the value into an environment variable. In other words, you could supply subdir=<whatever> as an environment to your original expression.
In any case, as bk2204 answered, you need to remove the -i option. Besides that, some versions of sed don't accept \t as a tab character (presumably yours does, just be aware of this).
To expand the variable earlier, just do that. For instance:
... --index-filter \
'git ls-files -s | sed "s-\t\"*-&'$folder'/-" | ...
(I've removed the -i here myself). Note how this exits single quotes, expands $folder, then re-enters single quotes. If $folder might contain whitespace, be sure to use double quotes while expanding it here:
... --index-filter \
'git ls-files -s | sed "s-\t\"*-&'"$folder"'/-" | ...
The nesting of quotes here is pretty tricky: the stuff inside the single quotes is all one big string, provided as the argument that sets the variable $filter_index inside the filter-branch script. The eval runs it through a second pass of evaluation, breaking up into the pipeline (git ls-files, piped to sed, piped to git update-index) and running the various multiple commands, all of which have their stdin redirected to /dev/null.
You're using sed -i, which edits its command-line arguments (which are names of files) in place. However, you're reading from standard input and haven't provided any command-line arguments. If you want to just filter the standard input like this, omit the -i from your sed command.

Change variable evaluation method in all script from $VAR_NAME to ${VAR_NAME}

We have couple of scripts where we want to replace variable evaluation method from $VAR_NAME to ${VAR_NAME}
This is required so that scripts will have uniform method for variable evaluation
I am thinking of using sed for the same, I wrote sample command which looks like follows,
echo "\$VAR_NAME" | sed 's/^$[_a-zA-Z0-9]*/${&}/g'
output for the same is
${$VAR_NAME}
Now i don't want $ inside {}, how can i remove it?
Any better suggestions for accomplishing this task?
EDIT
Following command works
echo "\$VAR_NAME" | sed -r 's/\$([_a-zA-Z]+)/${\1}/g'
EDIT1
I used following command to do replacement in script file
sed -i -r 's:\$([_a-zA-Z0-9]+):${\1}:g' <ScriptName>
Since the first part of your sed command searches for the $ and VAR_NAME, the whole $VAR_NAME part will be put inside the ${} wrapper.
You could search for the $ part with a lookbehind in your regular expression, so that you end up ending the sed call with /{&}/g as the $ will be to the left of your matched expression.
http://www.regular-expressions.info/lookaround.html
http://www.perlmonks.org/?node_id=518444
I don't think sed supports this kind of regular expression, but you can make a command that begins perl -pe instead. I believe the following perl command may do what you want.
perl -p -e 's/(?<=\$)[_a-zA-Z0-9]*/{$&}/g'
PCRE Regex to SED

Variables and escaping in RPM SPEC file macros?

I want to define a macro that will replace some placeholders in makefiles and systemd unit files with the results of RPM variables (macros). However, I don't know if the way expansion works will make the following behave correctly:
%define repl_vars() (sed -e "s:\${LIBEXECDIR}:%{_libexecdir}:g" -e "s:\${LOCALSTATEDIR}:%{_localstatedir}:g" -e "s:\${SYSCONFIGDIR}:%{_sysconfdir}:g" %{1} > %{1}.new && mv %{1}.new %{1})
Where the capitalized ${...} are the placeholders to be replaced with the actual paths held by the standard RPM variables (macros).
Also, escaping the $ of the placeholders with \ works in Bash and stuff I put in the %install section of the SPEC file, but is that still valid in a macro? And is the %{1} valid, as I've never seen an example -- and if not, how do I concatenate .new to %1?
If this is wrong, how do I do it?
tl;dr Yes, that looks basically correct to me. See http://www.rpm.org/wiki/PackagerDocs/Macros for some (somewhat outdated but still largely relevant) documentation.
Macros that are to be expanded in a shell context in a spec file just want to expand to the literal lines that you would write in the spec file section manually.
So assuming you want something to the effect of:
%install
....
sed -e 's:${LIBEXECDIR}:%{_libexecdir}:g' -e 's:${LOCALSTATEDIR}:%{_localstatedir}:g' -e 's:${SYSCONFIGDIR}:%{_sysconfdir}:g' 'some_file' > 'some_file.new' && mv 'some_file.new' 'some_file'
....
And you want to call your macro as %repl_vars some_file then you want a macro roughly like this:
%define repl_vars() sed -e 's:${LIBEXECDIR}:%{_libexecdir}:g' -e 's:${LOCALSTATEDIR}:%{_localstatedir}:g' -e 's:${SYSCONFIGDIR}:%{_sysconfdir}:g' '%{1}' > '%{1}.new' && mv '%{1}.new' '%{1}'
Notice I switched to single quotes instead of double quotes to avoid $ from being evaluated and needing to be escaped. I also dropped the wrapping () because this didn't seem to need the forced sub-shell.

Extract all variable values in a shell script

I'm debugging an old shell script; I want to check the values of all the variables used, it's a huge ugly script with approx more than 140 variables used. Is there anyway I can extract the variable names from the script and put them in a convenient pattern like:
#!/bin/sh
if [ ${BLAH} ....
.....
rm -rf ${JUNK}.....
to
echo ${BLAH}
echo ${JUNK}
...
Try running your script as follows:
bash -x ./script.bash
Or enable the setting in the script:
set -x
You can dump all interested variables in one command using:
set | grep -w -e BLAH -e JUNK
To dump all the variables to stdout use:
set
or
env
from inside your script.
You can extract a (sub)list of the variables declared in your script using grep:
grep -Po "([a-z][a-zA-Z0-9_]+)(?==\")" ./script.bash | sort -u
Disclaimer: why "sublist"?
The expression given will match string followed by an egal sign (=) and a double quote ("). So if you don't use syntax such as myvar="my-value" it won't work.
But you got the idea.
grep Options
-P --perl-regexp: Interpret PATTERN as a Perl regular expression (PCRE, see below) (experimental) ;
-o --only-matching: Print only the matched (non-empty) parts of a matching line, with each such part on a separate output line.
Pattern
I'm using a positive lookahead: (?==\") to require an egal sign followed by a double quote.
In bash, but not sh, compgen -v will list the names of all variables assigned (compare this to set, which has a great deal of output other than variable names, and thus needs to be parsed).
Thus, if you change the top of the script to #!/bin/bash, you will be able to use compgen -v to generate that list.
That said, the person who advised you use set -x did well. Consider this extension on that:
PS4=':$BASH_SOURCE:$LINENO+'; set -x
This will print the source file and line number before every command (or variable assignment) which is executed, so you will have a log not only of which variables are set, but just where in the source each one was assigned. This makes tracking down where each variable is set far easier.

How to search and replace text in a file from a shell script?

I'm trying to write a shell script that does a search and replace inside a configuration file upon start-up.
The string we're trying to replace is:
include /etc/nginx/https.include;
and we want to replace it with a commented version:
#include /etc/nginx/https.include;
The file that contains the string that we want to replace is:
/etc/nginx/app-servers.include
I'm not a Linux guru and can't seem to find the command to do this.
perl -p -i -e 's%^(include /etc/nginx/https.include;)$%#$1%' /etc/nginx/ap-servers.include
If the line might not end in the ;, use instead:
perl -p -i -e 's%^(include /etc/nginx/https.include;.*)$%#$1%' /etc/nginx/ap-servers.include
If you want to preserve the original file, add a backup extension after -i:
perl -p -i.bak -e 's%^(include /etc/nginx/https.include;)$%#$1%' /etc/nginx/ap-servers.include
Now, explaining. The -p flag means replace in-place. All lines of the file will be fed to the expression, and the result will be used as replacement. The -i flag indicates the extension of the backup file. By using it without anything, you prevent generation of backups. The -e tells Perl to get the following parameter as an expression to be executed.
Now, the expression is s%something%other%. I use % instead of the more traditional / to avoid having to escape the slashes of the path. I use parenthesis in the expression and $1 in the substituted expression for safety -- if you change one, the other will follow. Thus, %#$1% is actually the second % of s, followed by the desired #, $1 indicating the pattern inside parenthesis, and the last % of s.
HTH. HAND.
sed -i 's/foo/bar/g' config.txt
This replaces all instances of foo (case insensitive) with bar in the file config.txt
Check out sed:
sed -i -r 's|^(include /etc/nginx/https.include;)$|#\1|' /etc/nginx/app-servers.include
-i means do the substitution in-place and -r means to use extended regular expressions.
cd pathname
for y in `ls *`;
do sed "s/ABCD/DCBA/g" $y > temp; mv temp $y;
done
This script shold replace string ABCD to DCBA in all the files in pathname

Resources