Why don't substrings work with command line arguments? - string

In a Windows batch file, the following will work to extract all of %1 except the last 4 characters:
set foo=%1
set x=%foo:~,-4%
But this will not work:
set x=%1:~,-4%
Why is this?

The Windows command processor cmd.exe supports string substitutions only with environment variables (and with dynamic variables), but not with batch file arguments (or with loop variables).
foo is an environment variable of which value is referenced with immediate expansion using %foo% and with delayed expansion with !foo!. String substitutions are supported for environment variables as described by the help output on running set /? in a command prompt window. The Windows command processor cmd.exe supports string substitutions on environment (and dynamic) variable expansions everywhere in a command line.
For more details see How does the Windows Command Interpreter (CMD.EXE) parse scripts?
For an explanation of the difference between environment and dynamic variables read the long answer on Difference between Dynamic Environment Variables and Normal Environment Variables in CMD.
%1 references an argument passed to the batch file. The help output on running call /? in a command prompt window explains how arguments can be referenced in a batch file and which modifiers are supported by the Windows command processor. String substitutions are not supported by cmd.exe on argument strings.
For completeness the help output on running for /? explains how a loop variable can be referenced inside the body of a loop and which modifiers are available (the same as for argument references). String substitutions are not support for loop variables.

Related

How to pass a QMAKE variable from the command line?

I am to trying cross-compile pile Qt from a Linux terminal. When I run qmake it applies the mkspecs qmake.conf in my context in such manner that the CROSS_COMPILE variable must be defined. For example, there is a critical conf line that looks like this:
QMAKE_CXX = $${CROSS_COMPILE}g++
Qmake returns an error though which clearly indicates $${CROSS_COMPILE} is not being resolved. It is simply using "g++" instead of the whole value which ought to be there.
I've tried to invoke qmake and define the variable from a bash script like this:
qmake qt.pro "CROSS_COMPILE=${CROSS_COMPILE}"
And like this :
qmake qt.pro -- "CROSS_COMPILE=${CROSS_COMPILE}"
And a few other such stabs at it. I've also tried hard coding the value in that command in case that had anything to do with it. I've tried defining this as an environmental variable too (just in case)...
Nothing works. Yet, I've seen piles of examples where this syntax seems to be valid. What am doing wrong? Could there be a character escape complication?
Your problem is that the shell already interpreted the ${} inside your string as a form of variable substitution.
Since you did not define the variable CROSS_COMPILE in the shell, it had no value and what qmake got were actually the 2 arguments between quotes "qt.pro" and "CROSS_COMPILE=", meaning that you have actually made qmake set CROSS_COMPILE to an empty value.
What you should try is:
qmake qt.pro "CROSS_COMPILE=\${CROSS_COMPILE}"
Note the backslash before the dollar sign, which escapes it to prevent it from having a special meaning to the shell and enables it to get passed on literally to qmake.
This question has also been already asked on Stackoverflow:
Define a string in qmake command line
More on the variable substitution of Bash:
https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html
EDIT:
Example:
I just tried myself with a fresh project file with the following contents:
SOME_OTHER_VAR=$${SOME_VAR}_something
message($${SOME_OTHER_VAR})
and doing
SOME_VAR=value
qmake qmake_variables.pro "SOME_VAR=${SOME_VAR}"
does work for me, printing:
Project MESSAGE: value_something
This is not the best answer, but I "solved" the problem by adding this to my qmake.conf:
CROSS_COMPILE=$$(CROSS_COMPILE)
That defined the variable in qmake by getting it from an environmental variable I set in my calling bash script.

can we pass options to tcl source command in tcl 8.5

I am using this command to source get.tcl file and giving options 'verbose' and 'instant':
source -verbose -instant get.tcl
the above command worked for me in tcl 8.4 but showing this error in tcl 8.5
source (script wrong # args: should be "source_orig ?-encoding name?
fileName"
if I write only
source get.tcl
It get passed in tcl 8.5
Is there any change related to this in tcl 8.5?
The source command only accepts one option (since 8.5), -encoding, which is used to specify what encoding the file being read is in (instead of the default guess of encoding as returned by encoding system). All it does is read the file into memory and (internally-equivalent-to-) eval the contents.
You can write to any variable you want prior to doing the source, including global variables like argv. With that (plus appropriate use of uplevel and catch, as required, and maybe also interp create) you can simulate running the script as a subprocess. But it's probably easier to not have the file expect to be handling arguments like that, and instead for it to define a command that you call immediately after the sourcing.
You can pass arguments to your sourced file by doing the following:
set ::argv [list -verbose -instant]
source get.tcl
I recommend using:
set ::argv [list -- -verbose -instant]
The -- will stop tclsh from processing any arguments after the --.
Sometimes tclsh will recognize an argument that is meant for your
program and process it. Your programs will need to know about
the -- and handle it appropriately.

How to get the complete calling command of a BASH script from inside the script (not just the arguments)

I have a BASH script that has a long set of arguments and two ways of calling it:
my_script --option1 value --option2 value ... etc
or
my_script val1 val2 val3 ..... valn
This script in turn compiles and runs a large FORTRAN code suite that eventually produces a netcdf file as output. I already have all the metadata in the netcdf output global attributes, but it would be really nice to also include the full run command one used to create that experiment. Thus another user who receives the netcdf file could simply reenter the run command to rerun the experiment, without having to piece together all the options.
So that is a long way of saying, in my BASH script, how do I get the last command entered from the parent shell and put it in a variable? i.e. the script is asking "how was I called?"
I could try to piece it together from the option list, but the very long option list and two interface methods would make this long and arduous, and I am sure there is a simple way.
I found this helpful page:
BASH: echoing the last command run
but this only seems to work to get the last command executed within the script itself. The asker also refers to use of history, but the answers seem to imply that the history will only contain the command after the programme has completed.
Many thanks if any of you have any idea.
You can try the following:
myInvocation="$(printf %q "$BASH_SOURCE")$((($#)) && printf ' %q' "$#")"
$BASH_SOURCE refers to the running script (as invoked), and $# is the array of arguments; (($#)) && ensures that the following printf command is only executed if at least 1 argument was passed; printf %q is explained below.
While this won't always be a verbatim copy of your command line, it'll be equivalent - the string you get is reusable as a shell command.
chepner points out in a comment that this approach will only capture what the original arguments were ultimately expanded to:
For instance, if the original command was my_script $USER "$(date +%s)", $myInvocation will not reflect these arguments as-is, but will rather contain what the shell expanded them to; e.g., my_script jdoe 1460644812
chepner also points that out that getting the actual raw command line as received by the parent process will be (next to) impossible. Do tell me if you know of a way.
However, if you're prepared to ask users to do extra work when invoking your script or you can get them to invoke your script through an alias you define - which is obviously tricky - there is a solution; see bottom.
Note that use of printf %q is crucial to preserving the boundaries between arguments - if your original arguments had embedded spaces, something like $0 $* would result in a different command.
printf %q also protects against other shell metacharacters (e.g., |) embedded in arguments.
printf %q quotes the given argument for reuse as a single argument in a shell command, applying the necessary quoting; e.g.:
$ printf %q 'a |b'
a\ \|b
a\ \|b is equivalent to single-quoted string 'a |b' from the shell's perspective, but this example shows how the resulting representation is not necessarily the same as the input representation.
Incidentally, ksh and zsh also support printf %q, and ksh actually outputs 'a |b' in this case.
If you're prepared to modify how your script is invoked, you can pass $BASH_COMMANDas an extra argument: $BASH_COMMAND contains the raw[1]
command line of the currently executing command.
For simplicity of processing inside the script, pass it as the first argument (note that the double quotes are required to preserve the value as a single argument):
my_script "$BASH_COMMAND" --option1 value --option2
Inside your script:
# The *first* argument is what "$BASH_COMMAND" expanded to,
# i.e., the entire (alias-expanded) command line.
myInvocation=$1 # Save the command line in a variable...
shift # ... and remove it from "$#".
# Now process "$#", as you normally would.
Unfortunately, there are only two options when it comes to ensuring that your script is invoked this way, and they're both suboptimal:
The end user has to invoke the script this way - which is obviously tricky and fragile (you could however, check in your script whether the first argument contains the script name and error out, if not).
Alternatively, provide an alias that wraps the passing of $BASH_COMMAND as follows:
alias my_script='/path/to/my_script "$BASH_COMMAND"'
The tricky part is that this alias must be defined in all end users' shell initialization files to ensure that it's available.
Also, inside your script, you'd have to do extra work to re-transform the alias-expanded version of the command line into its aliased form:
# The *first* argument is what "$BASH_COMMAND" expanded to,
# i.e., the entire (alias-expanded) command line.
# Here we also re-transform the alias-expanded command line to
# its original aliased form, by replacing everything up to and including
# "$BASH_COMMMAND" with the alias name.
myInvocation=$(sed 's/^.* "\$BASH_COMMAND"/my_script/' <<<"$1")
shift # Remove the first argument from "$#".
# Now process "$#", as you normally would.
Sadly, wrapping the invocation via a script or function is not an option, because the $BASH_COMMAND truly only ever reports the current command's command line, which in the case of a script or function wrapper would be the line inside that wrapper.
[1] The only thing that gets expanded are aliases, so if you invoked your script via an alias, you'll still see the underlying script in $BASH_COMMAND, but that's generally desirable, given that aliases are user-specific.
All other arguments and even input/output redirections, including process substitutiions <(...) are reflected as-is.
"$0" contains the script's name, "$#" contains the parameters.
Do you mean something like echo $0 $*?

What is the list file (*.f) for verilog?

I found both ncvlog and Verdi can read the design through *.f which includes *.v files and +incdir commands. It's easy to get an example and modify it fit the new project.
However, is there have any specific description about .f file?
Commonly referred to as "dot-f" files, files that end with an extension of .f contain command-line arguments for the simulator. The .f extension is actually just a convention and not required by the tools. The the file is passed in with a -f or -F option.
Any command-line argument that the tool accepts can be placed within a file that is passed with the -f option.
Here is an excerpt from an old ncvlog manual I found online:
-File arguments_filename
Use the command-line arguments contained in the specified arguments file.
You can store frequently used or lengthy command lines by putting command-line arguments
(command options and top-level design unit names) in a text file. When you invoke the
elaborator with the -file option, the arguments in the arguments file are incorporated with
your command as if they had been entered on the command line.
The arguments file can contain command options, including other -file options, and
top-level design unit names. The individual arguments within the arguments file must be
separated by white space or comments.
As an example, the following two scenarios are equivalent:
Specify command-line arguments directly
$> ncvlog +incdir+foo mod1.v mod2.v mod3.v
Specify command-line arguments in a .f file
args.f:
+incdir+foo
mod1.v
mod2.v
mod3.v
$> ncvlog -f args.f
it's just some arguments, you can put file list, include directory, macro define, and other option here

Read / Run Environment Variables in a text file

I have an app which runs and reads a text configuration file.
This points to several locations of configurations / outputs etc.
Is it possible to use environmental variables inside the text configuration file, rather than hardcoded paths?
LogFilePath=$LOG_FILE_PATH
vs
LogFilePath=/home/user/logs
When running, the application fails as it cannot expand the Environment Variable.
It will be sourced inside the shell before the application is run.
Thanks!
Recently I used this in a (bash) script:
#!/bin/bash
# ...
source config.file
# ...
Where config.file had lines like this:
export ORIG_PATH="${PATH:-.}:/bla/bla"
export SOMESETTING="${SOMEVAR:-"somedefault"},somedata"
...
So the ${parameter:-word} thing worked well for me: use default values, if parameter is unset or null, the expansion of word is substituted. Otherwise, the value of parameter is substituted. (From man bash.)
HTH

Resources