I am seeing a number of rules in a Makefile that look like this:
$(PATH)/foo.inc:;
include $(PATH)/foo.inc
$(PATH)/bar.inc:;
include $(PATH)/bar.inc
Is the semi-colon at the end of the rule definition a no-op or does it have a particular meaning?
A semicolon on the line with the target-prerequisite is the first command line to execute for this rule, at least in GNU make.
From chapter 5 of the manual:
The commands of a rule consist of shell command lines to be executed
one by one. Each command line must start with a tab, except that the
first command line may be attached to the target-and-prerequisites
line with a semicolon in between.
In your case since there is no command after the semi-colon then it ends up being a no-op.
Related
Suppose there's a text file with the following line:
export MYSQL_ADMIN=''
I want to insert text inside that single quote using the sed command, so that it changes to something like this for example:
export MYSQL_ADMIN='abc1'
What is the appropriate sed command for that in Linux?
I tried
sed -i -e ''/MYSQL_ADMIN/s/''/'abc1'/g"
but it didn't work.
Something like sed -i "s;export MYSQL_ADMIN=.*;export MYSQL_ADMIN='abc1';" /path/to/file.ext
-i modify file in place
s means substitute,
First block is what you are matching as an regular expression - the .* matches everything to the end of the line, this ensures you don't keep any text on that line after the substitue - and second block is what you are replacing with that match.
Always check the file after each run of sed if there is no error and check what changed.
To get the single quotes to print you may have to do ""'"" like ""'""abc1""'""
It is important to understand that although
I want to insert text inside that single quote using the sed command
is a perfectly good characterization of the effect you want to achieve, it does not map directly onto operations from sed's repertoire. With sed, the appropriate tool for most line modifications is the s command, which substitutes specified text for one or more matches to a specified regular expression. That would be the most natural thing to use for your case.
Additionally, it is important with sed to understand how and when to bind commands to specific lines. If you don't do that for a given command then it is applied to all lines. Sometimes that's fine, but other times it will produce unwanted results.
I tried
sed -i -e ''/MYSQL_ADMIN/s/''/'abc1'/g"
but it didn't work.
The two leading single quotes in that sed expression match each other, leaving the trailing double quote unmatched. Also, you do not specify the name of the file to modify. This variation would at least be valid shell syntax, and it would have the desired effect on the specified line appearing in file my_script:
sed -i -e "/MYSQL_ADMIN/s/''/'abc1'/g" my_script
That might also make other, unwanted changes, however.
You need to make some assumptions about the content of the file in order to do such a thing at all. The above depends on the text MYSQL_ADMIN and '' to appear on the same line only in the line(s) you want to modify. That may turn out to hold, but it seems unnecessarily risky. An assumption more likely to hold in general would be that there will be only one assignment to variable MYSQL_ADMIN, or that it is acceptable to modify all such assignments that assign a single-quote-delimited empty value.
Going with the latter, one might end up with this:
sed -i -e "s/\<MYSQL_ADMIN=''\(\s\|$\)/MYSQL_ADMIN='abc1'\1/g" my_script
The pattern \<MYSQL_ADMIN=''\(\s\|$\) improves on your plain MYSQL_ADMIN in these significant ways:
the \< causes it to match only immediately after a word boundary -- start of line, whitesepace, or punctuation. This prevents substitutions for other variables whose names happen to end with MYSQL_ADMIN. If you prefer, it would be even stronger to instead anchor the match to the beginning of the line with ^.
including the ='' in the pattern distinguishes between MYSQL_ADMIN and variables whose names contain that as an initial substring. It also ensures that the '' that gets replaced, if any, goes with the variable and does not merely appear somewhere else on the line.
the \(\s\|$\) both matches and captures either a whitespace character or the empty string at the end of a line. This distinguishes between assignments of an empty value and assignments of values that are merely prefixed by '' (which is valid if the file is a shell script). Having included it in the match, the capture allows the matched text, if any, to be preserved in the output (via the \1 in the replacement).
Because that matches the whole assignment, a complete assignment must appear in the replacement, too. On the other hand, this means that (probably) you can apply the command to every line, as shown, with no particular loss of efficiency relative to the previous command.
Even that might produce changes you didn't want, however, such as in comment lines or quoted text.
I am trying to run 'make' command from my bash script to build the code.
I can see that all parameters got assigned and able to display the command that i am trying to run. I could not see any issue with the command. But the issue is when it tries to run the command via bash script it fails.
My command is :- ./build_script LIC=1 DOCUMNETS=1 PROJECTS="cuda bfm"
Script Snippet of parsing all the arguments and constructing make command:-
makeargs=""
for build_flag do
if [[ "$build_flag " = "PROJECTS="* ]]; then
apps =`echo $build_flag |sed "s/PROJECTS=//" `
makeargs="$makeargs PROJECTS=\"$apps \""
else
makeargs="$makeargs $build_flag"
fi
done
echo "make DCOV=1 $makeargs $maketest"
make DCOV=1 $makeargs $maketest
When i run the script, I can see the build command has constructed properly.
Output :-
make DCOV=1 LIC=1 DOCUMNETS=1 PROJECTS="cuda bfm" run_all
GNUmakefile:16: warning: overriding commands for target `/'
GNUmakefile:19: warning: ignoring old commands for target `/'
make: *** No rule to make target `bfm"'. Stop.
I try to print PROJECTS variable in my 'GNUmakefile' present in build_main folder. I can see the output : PROJECTS is "bfm . It is not taking whole "cuda bfm" as a whole string.
Note:- When i try to run the same build command :- make DCOV=1 LIC=1 DOCUMNETS=1 PROJECTS="cuda bfm" run_all explicitly it works fine.
Seems like issue with Interpreting variables with makefile.
Any solution for this ? Please help.
Thanks!
Change makeargs string to array before passing it as an arguments group.
eval makeargs_array=( $makeargs )
make UVC=1 "${makeargs_array[#]}" $maketest
Without converting to array, if you enable debug, it shows last line interpretation as
make DCOV=1 LIC=1 DOCUMNETS=1 'PROJECTS="cuda' bfm '"'
Which is clearly ignoring double-quote and considering space as separator.
Even double-quote is getting passed as a separate argument in this case.
Explanation:
Word-splitting
It says,
The shell scans the results of parameter expansion, command
substitution, and arithmetic expansion that did not occur within
double quotes for word splitting.
If we use "$makeargs" i.e. surrounded by double-quote, it is not considered by word-splitting and results in LIC=1 DOCUMNETS=1 "PROJECTS=cuda bfm"
But again its a complete string, while requirement is to split the string to use as arguments.
So now using $makeargs.
Word-splitting gets in action as per the default IFS (space, tab, newline), we get result as LIC=1 DOCUMNETS=1 PROJECTS="cuda bfm "
Double-quoted part of string didn't affect the word-splitting since, subject to splitting is complete string here.
Why array worked here?
Array itself expands each element as separate word when using # and here no further word-splitting requires after expansion.
Arrays
I am trying to execute script with commands:
sed -i "USER/c\export USER=${signumid}" .bashrc
sed -i "DEVENVHOME=$/c\export DEVENVHOME=${DEVENVHOME:-/home/${signumid}/CPM_WORKAREA/devenv.x}" .bashrc
I want to replace the line with string "USER" in .bashrc with export USER=${signumid} where $signumid variable is being provided through Cygwin prompt. Similarly I want to replace line with string DEVENVHOME=$ with export DEVENVHOME=${DEVENVHOME:-/home/${signumid}/CPM_WORKAREA/devenv.x} in .bashrc file, where $signumid variable is provided through Cygwin prompt.
But I am getting following errors on Cygwin termminal.:
sed: -e expression #1, char 1: unknown command: `U'
sed: -e expression #1, char 3: extra characters after command
The general syntax of a sed script is a sequence of address command arguments statements (separated by newline or semicolon). The most common command is the s substitution command, with an empty address, so we can perhaps assume that that is what you want to use here. You seem to be attempting to interpolate a shell variable $signumid which adds a bit of a complication to this exposition.
If your strings were simply static text, it would make sense to use single quotes; then, the shell does not change the text within the quotes at all. The general syntax of the s command is s/regex/replacement/ where the slash as the argument separator is just a placeholder, as we shall soon see.
sed -i 's/.*USER.*/export USER=you/
s% DEVENVHOME=\$%export DEVENVHOME=${DEVENVHOME:-/home/you/CPM_WORKAREA/devenv.x}%' .bashrc
This will find any line with USER and substitute the entire line with export USER=you; then find any line which contains DEVENVHOME=$ (with a space before, and a literal dollar character) and replace the matched expression with the long string. Because the substitution string uses slashes internally, we use a different regex separator % -- alternatively, we could backslash-escape the slashes which are not separators, but as we shall see, that quickly becomes untenable when we add the following twist. Because the dollar sign has significance as the "end of line" metacharacter in regular expressions, we backslash-escape it.
I have ignored the c\ in your attempt on the assumption that it is simply a misunderstanding of sed syntax. If it is significant, what do you hope to accomplish with it? c\export is not a valid Bash command, so you probably mean something else, but I cannot guess what.
Now, to interpolate the value of the shell variable signumid into the replacement, we cannot use single quotes, because those inhibit interpolation. You have correctly attempted to use double quotes instead (in your edited question), but that means we have to make some additional changes. Inside double quotes, backslashes are processed by the shell, so we need to double all backslashes, or find alternative constructs. Fortunately for us, the only backslash is in \$ which can equivalently be expressed as [$], so let's switch to that notation instead. Also, where a literal dollar sign is wanted in the replacement string, we backslash-escape it in order to prevent the shell from processing it.
sed -i "s/.*USER.*/export USER=$signumid/
s% DEVENVHOME=[$]%export DEVENVHOME=\${DEVENVHOME:-/home/$signumid/CPM_WORKAREA/devenv.x}%" .bashrc
Equivalenty, you could use single quotes around the parts of the script which are meant to be untouched by the shell, and then put an adjacent double-quoted string around the parts which need interpolation, like
'un$touched*by$(the!shell)'"$signumid"'more$[complex]!stuff'
This final script still rests on a number of lucky or perhaps rather unlucky guesses about what you actually want. On the first line, I have changed just USER to a regular expression which matches the entire line -- maybe that's not what you want? On the other hand, the second line makes the opposite assumption, just so you can see the variations -- it only replaces the actual text we matched. Probably one or the other needs to be changed.
Finally, notice how the two separate sed commands have been conflated into a single script. Many newcomers do not realize that sed is a scripting language which accepts an arbitrary number of commands in a script, and simply treat it as a "replace" program with a funny syntax.
Another common source of confusion is the evaluation order. The shell processes the double-quoted string even before sed starts to execute, so if you have mistakes in the quoting, you can easily produce syntax errors in the sed script which lead to rather uninformative error messages (because what sed tells you in the error message is based on what the script looks like after the shell's substutions). For example, if signumid contains slashes, it will produce syntax errors, because sed will see those as terminating separators for the s/// command. An easy workaround is to switch to a separator which does not occur in the value of signumid.
I read the following command from the batch file to run Maven on Windows mvn.bat:
if not "_%M2_HOME:~-1%"=="_\" goto checkMBat
And
if "%#eval[2+2]" == "4" goto 4NTArgs
What does this batch script mean?
ADD 1
As I tried, it seems _%M2_HOME:~-1% returns the _ plus the last 1 letter of the environment variable "_%M2_HOME%. But what's the name of this syntax?
%VAR:~-1% gets the last character in the envvar. The first snippet verifies that the envvar M2_HOME doesn't end with \. Note: Maven's docs say,
Note: For Maven 2.0.9, also be sure that the M2_HOME doesn't have a '\' as last character.
This might be related. They probably want to prepend M2_HOME to subdir names and always include a dirsep. The variable substitution in "_%...%" is unaffected by the initial underscore. Experessing it that way just ensures that the underscore is at the beginning of the output. I can't say for certain, but it may have been expressed that way to avoid a backslashed quote, e.g. "\".
The second is not any CMD/batch that I'm familiar with. The comment (assuming this comes from mvn.bat) says "4NT shell", which I take to mean that this batch file could be run in the Take Command Console which probably has extensions to MS CMD features. For example, %#eval[...] probably does numeric evaluation in 4NT. This would effectively be a check to see if the script were running in a 4NT shell.
The first one takes the last character of %M2_HOME%, adds an underscore to the front, and checks to see if the resulting string is _\ - in short, it checks that the last character of %M2_HOME% is a backslash by using substrings.
The second one is how you determine if 4NT is installed on your computer; if it is, there will be a variable function called #eval.
I found the explanation to "_%M2_HOME:~-1%" below link. It's a variable substring operation.
http://ss64.com/nt/syntax-substring.html
I read this question: Makefile: $subst in dependency list, but I still can't make my shell script work correctly.
I have a makefile with a line with the contents:
##public_detailed#|test_create|Syntax: commoncmdsyntax test_create test_name=<test-name>
A target runs a multiline bash script, where the commoncmdsyntax must be replaced by a string containing words and spaces.
In the script, I use cut to assign to a variable desc the following string:
Syntax: commoncmdsyntax test_create test_name=<test-name>
The problem is that commoncmdsyntax is not replaced by new text here:
$(subst commoncmdsyntax,new text,$$desc)
I also tried to replace it by a single word, like XX, but it also does not work.
The subst function (as in $(subst commoncmdsyntax,new text,$$desc)) is a Make function, so Make will perform the substitution before running any rule and therefore before your script assigns a value to desc. So even if secondary expansion worked the way you seem to think it will, this approach would still fail.
If you want to perform a substitution within something made by a shell script (in a recipe), the sensible way is to do so within the recipe:
echo $dest | sed 's/commoncmdsyntax/new text/'
We can give you a more detailed solution if you give us a minimal complete example of the problem.