extract variable's value from script file in Install Anywhere - linux

I am using Install Anywhere 2012 and would like to be able to parse a batch or shell script for a give value and have that value stored in an IA variable. For instance, if I have the following shell file:
MY_VAR1=123
MY_VAR2=a\b\c
ECHO $MY_VAR1
I would like to pass in the path to the file and the variable name (ex. MY_VAR1) and have the result, 123, stored in an IA variable of my choosing (lets say $OUTPUT$). I could achieve this through writing some java custom code but was wondering if there was an alternative approach built into IA that would make this much easier. The variable will not be initialized when I need to figure out its value so essentially just echoing it's value or something similar will not work. Any help would be greatly appreciated!

example in Windows batch:
#echo off &setlocal
for /f "tokens=2delims==" %%a in ('findstr "MY_VAR1" "ShellFile"') do set "output=%%a"
if defined output (echo MY_VAR1: %output%) else echo MY_VAR1 not found!

On Linux/Unix, you could use perl or awk (both are standard utilities in most distros). Python or Ruby are also candidates, but may not be installed on your target system. You could even write your own targeted parser using Lex and Yacc and ship it with your installer. However, for your needs, that's surely overkill.
Here's an example of a possible awk solution in an Execute Script/Batch File Action:
#!/bin/bash
awk '
# Process lines that begin with our variable name,
# preceded by optional spaces or tabs.
/^[ \t]*$TARGET_VARIABLE_NAME$=.+/ {
# Split the current line on "=" into
# an array called result
split($0, result, "=")
value = result[1]
# Look for trailing comments and remove them.
offset = index(value, "#")
if (offset > 0) {
value = substr(value, 1, offset - 1)
}
# Remove any possible leading spaces and quotes.
# Note that the single-quote is escaped. That escape
# is for bash, not for awk. I am doing this from
# memory and do not have access to IA right now.
# you may have to play with the escaping.
gsub(/^[\'" ]*/, "", value)
# Remove any possible trailing spaces and quotes.
# See above regarding the escaped single-quote.
gsub(/[\'" ]*$/, "", value)
# send "value" to stdout
print value
}
' < $SHELL_INPUT_FILE$
The print value line (near the end) sends value to stdout.
In the Execute Script/Batch File Action settings you can designate variables that receive the stdout and stderr streams produced by the script action. By default, the stdout stream is stored in $EXECUTE_STDOUT$. You can change this to a variable name of your choosing.
In the example, above, $TARGET_VARIABLE_NAME$ and $SHELL_INPUT_FILE$ are InstallAnywhere variables that hold the name of the variable to find and the name of the file to parse, respectively. These variables will be replaced by their values before the Action executes.
Assume we have a script called /home/fred/hello.sh, which contains the following code:
#!/bin/bash
WIFE='Wilma'
NEIGHBOR="Barney Rubble"
echo "Hello to $WIFE and $NEIGHBOR from $PWD"
Before the Execute Script/Batch File Action runs, stuff the name of the script file into $SHELL_INPUT_FILE$ (/home/fred/hello.sh). Then set the value of $TARGET_VARIABLE_NAME$ to the variable you wish to find (say, NEIGHBOR). After the action completes, $EXECUTE_STDOUT$ in InstallAnywhere will contain Barney Rubble.
You can build on this idea to parse arbitrarily complex files in an Execute Script/Batch File Action. Just make your awk (or perl/Ruby/Python) script as complex as needed.
NOTE: when scripting Unix shell scripts in InstallAnywhere ALWAYS check the "Do not replace unknown variables" option. If you don't, InstallAnywhere will quietly convert anything that looks vaguely like an InstallAnywhere variable into blanks... It's very annoying.
For a Windows solution, find a standalone Windows version of awk or perl and include it with your installation. Then extend the above solution to work for batch files.
You'd want to create two Execute Script/Batch File Actions, one with a rule for Linux/Unix and one with a rule for Windows. You'd have to install the Windows awk or perl executable before calling this action though. Also, you'd need to fully qualify the path to the awk/perl executable. Finally, the actual script will need to be sensitive to differences in batch syntax versus shell syntax.
Below is an awk script modified to look for batch variable definitions. The pattern changes and you won't have to worry about embedded comments:
$PATH_TO_AWK_EXE$ '
# This pattern looks for optional spaces, the word SET
# with any capitalization, the target variable, more
# optional spaces and the equals sign.
/^[ \t]*[Ss][Ee][Tt][ \t]*$TARGET_VARIABLE_NAME$[ \t]*=.+/ {
# Split the current line on "=" into
# an array called result
split($0, result, "=")
value = result[1]
# No trailing comments in Batch files.
# Remove any possible leading spaces and quotes.
# Note that the single-quote is escaped. That escape
# is for bash, not for awk. I am doing this from
# memory and do not have access to IA right now.
# you may have to play with the escaping.
gsub(/^[\'" ]*/, "", value)
# Remove any possible trailing spaces and quotes.
# See above regarding the escaped single-quote.
gsub(/[\'" ]*$/, "", value)
# send "value" to stdout
print value
}
' < $SHELL_INPUT_FILE$
Above, the IA variable $PATH_TO_AWK_EXE$ points to the location where awk was installed. It would be set as some combination of $USER_INSTALL_FOLDER$, possibly other directory names, and the name of the awk.exe file. $PATH_TO_AWK_EXE$ can later be used to remove the awk executable, if desired.

You can try to get variable from output of script
"Execute Script" -> Store process's stdout in: $EXECUTE_OUTPUT$
Than you can use $EXECUTE_OUTPUT$ as variable after that

Related

Shell script (bash) to match a string variable with multiple values

I am trying write a script to compare one string variable to a list of values, i.e. if the variable matches (exact) to one of the values, then some action needs to be done.
The script is trying to match Unix pathnames, i.e. if the user enters / , /usr, /var etc, then to give an error, so that we do not get accidental corruption using the script. The list of values may change in future due to the application requirements. So I cannot have huge "if" statement to check this.
What I intend to do is that in case if the user enters, any of the forbidden path to give an error but sub-paths which are not forbidden should be allowed, i.e. /var should be rejected but /var/opt/app should be accepted.
I cannot use regex as partial match will not work
I am not sure of using a where loop and an if statement, is there any alternative?
thanks
I like to use associative arrays for this.
declare -A nonoList=(
[/foo/bar]=1
["/some/other/path with spaces"]=1
[/and/so/on]=1
# as many as you need
)
This can be kept in a file and sourced, if you want to separate it out.
Then in your script, just do a lookup.
if [[ -n "${nonoList[$yourString]}" ]] # -n checks for nonzero length
This also prevents you from creating a big file ad grep'ing over it redundantly, though that also works.
As an alternative, if you KNOW there will not be embedded newlines in any of those filenames (it's a valid character, but messy for programming) then you can do this:
$: cat foo
/foo/bar
/some/other/path with spaces
/and/so/on
Just a normal file with one file-path per line. Now,
chkSet=$'\n'"$(<foo)"$'\n' # single var, newlines before & after each
Then in your processing, assuming f=/foo/bar or whatever file you're checking,
if [[ "$chkSet" =~ $'\n'"$f"$'\n' ]] # check for a hit
This won't give you accidental hits on /some/other/path when the actual filename is /some/other/path with spaces because the pattern explicitly checks for a newline character before and after the filename. That's why we explicitly assure they exist at the front and end of the file. We assume they are in between, so make sure your file doesn't have any spaces (or any other characters, like quotes) that aren't part of the filenames.
If you KNOW there will also be no embedded whitespace in your filenames, it's a lot easier.
mapfile -t nopes < foo
if [[ " ${nopes[*]} " =~ " $yourString " ]]; then echo found; else echo no; fi
Note that " ${nopes[*]} " embeds spaces (technically it uses the first character of $IFS, but that's a space by default) into a single flattened string. Again, literal spaces before and behind key and list prevent start/end mismatches.
Paul,
Your alternative work around worked like a charm. I don't have any directories which need embedded space in them. So as long as my script can recognize that there are certain directories to avoid, it does its job.
Thanks

Trying to iterate through files stored in variables

I have to go through 2 files stored as variables and delete the lines which contain a string stored in another variable:
file1="./file1"
file2="./file2"
text="searched text"
for i in $file1,$file2; do
sed -i.txt '/$text/d' $i
done
The files to exist in the same folder as the script.
I get "No such file or directory". I have been stuck for the past 3 hours on this and honestly I'm pretty much about to quit Linux.
You have a several issues in your script. The right way to do is:
file1="./file1"
file2="./file2"
text="searched text"
for i in "$file1" "$file2"; do
sed -i.txt "/$text/d" "$i"
done
Issues:
for expects a space delimited list of arguments, not comma separated
it is important to enclose your variable expansions in double quotes to prevent word splitting
you need double quotes to enclose the sed expression since single quotes won't expand the variable inside
You could catch these issues through shellcheck and debug mode (bash -x script) as suggested by Charles.
Sorry to say that your shell script is not nicely design. In a shell scripts multi files should not be stored in multiple variables. Suppose you need to do the same operation on 100 different files what will you do? So follow the below style of code. Put all your file names in a file for example filelist.dat now see:-
First put all the file names in filelist.dat and save it
text="searched text"
while read file; do
sed -i.txt '/$text/d' $i
done < filelist.dat
Also not sure whether sed command will work like that. If not working make it like below:-
sed -i.txt 's|'"$text"'|d' $i

Command Line for Loop to Change Variables in File

I have a script that compiles and runs a piece of idl code. Looks like this,
arg1=$1
idl << EOF
.rnew testvalue_{arg1}.pro
testvalue_{arg1}.pro
EOF
I want to run a for loop from the command line is in which arg1 can take on different names. What I have so far is,
for arg1 in testvalue.sh; do arg1={'value1', 'value2'}; done
I don't think my logic is correct. What am I missing?
first, you need to place $ before variable name so bash knows that it is replaced
Also you just need to give testvalue.sh value, because it is copied from $1 to $arg1
But why I don't recommend creating new variables, just use $1 two times
So the testvalue.sh is:
idl << EOF
.rnew testvalue_$1.pro
testvalue_$1.pro
And the loop:
for arg in 'value1' 'value2'; do ./testvalue.sh $arg; done
I'm a little hazy on the exact details of the question, but it sounds like you want to put 'value1' and 'value2' inside the list of values the for-loop iterates:
for arg1 in value1 value2
do
idl <<EOF
.rnew testvalue_${arg1}.pro
testvalue_${arg1}.pro
EOF
done
Note that I've also changed {arg1} to ${arg1}, as the dollar sign is required to expand the variable.
A for loop in shell scripts will iterate over every value after the in keyword. In the example above it will set arg1 to value1, then execute the contents of the loop, then move on and set arg1 to value2 and execute again.
You can also store the values in a variable:
values_to_test="value1 value2"
for arg1 in $values_to_test
do
idl <<EOF
.rnew testvalue_${arg1}.pro
testvalue_${arg1}.pro
EOF
done
Bear in mind that this applies word splitting and path expansion to values_to_test, so you will need to ensure that none of the values contain question marks, square brackets, asterisks, spaces, tabs, or newlines.
If it worries you, you can disable path expansion (and thus allow use of question marks, square brackets and asterisks) by running set -f in the script before the loop runs.

properly using IO redirection to append user input to a file in linux scripting?

I'm just starting to learn linux scripting, and with them user output/inputs. One of the things i need to learn and keep trying to do to no avail is append user input to a file output. Something like
read text > text.dat
or
read text
$text > text.dat
Typically ends up in failure, or the creation of text.dat which ends up empty no matter what is typed in by the user. What am i missing?
The read command, as documented in it's manual file, will take a line of user input and assign it to a variable which you can name as an argument. It will also split the user input and assign it to multiple variables if you pass more than one name. It will do this all in the background without printing any kind of confirmation to the standard out. We also know that the > operator will redirect the standard out of a command to a file descriptor. It is also important to note that unless bash is explicitly told that a line contains multiple commands (by using a semi-colon or similar) it will assume it is all one command with multiple arguments.
So lets have a look at your examples and see what is happening:
read text > text.dat
This will run the read command, which will silently assign the user input to a variable called $text. It will then redirect the output of the command (nothing, as it is silent) to a file called text.dat. End result: an empty text.dat and an unused $text variable.
read text $text > text.dat
Bash will parse this command and first attempt to get the value assigned to the $text variable, at this point it is undefined and so it will be ignored. So it will run the read command, which will silently assign the user input to a variable called $text. It will then redirect the output of the command (nothing, as it is silent) to a file called text.dat. End result: an empty text.dat and an unused $text variable.
So how can we resolve this? The first command is fine, we use read text to allow the user to input a line and have that line assigned to a variable called $text. Then, we need a way to send that variable to standard out so we can redirect it. To do that, we can use the echo command, which we can redirect.
So for example:
read text
echo $text > text.dat
Another thing to note is that the > operator will overwrite the file, to append to it you can use the >> operator.
So to take a user input and append it to a file we have:
read text
echo $text >> text.dat

Windows Batch File - Need to remove line breaks from parameter

I have a powershell script that will run a query and export the results to Excel. I want to hook this into SQL Studio Management Studio (2008)'s external tools. SSMS will not allow powershell scripts so I am using a batch file to get it started.
In the external tools section you can specify some predefined arguments. The argument that I want is called "current text". This argument passes whatever is highlighted to the tool (batch file). My batch file then passes that argument on to a powershell script.
The problem is that if the user has a query that spans multiple lines highlighted then the powershell script fails because of the linebreaks. It seems it would be relatively easy to strip them out, or better yet, replace them with a space?
Here is my batch file:
echo %~1
call powershell ^& 'c:\temp\ExportSQL.ps1' -query "%~1"
My question is how can I replace newlines and carriage returns from %1 with a space before passing it to the powershell script? TIA.
First, I can't believe that you have really linefeeds in your parameter %1, as it is possible, but a bit complex to achieve this.
To control this you could use
echo on
rem # %1 #
To handle CR and linefeed characters you have to use the delayed expansion, as percent expansion of such a content is not possible.
With percent expansion CR's are always removed, linefeeds removes the rest of the line or in a block context they append a new command line.
So it's not possible to handle content with linefeeds in a parameter like %1 ..%9.
You need it stored in a variable.
Assuming you have content with newlines in a variable, you could replace it with spaces.
The empty lines in this example are important for the result.
#echo off
set text=This contains a ^
newline
setlocal EnableDelayedExpansion
set ^"result=!text:^
= !"
echo Content of text is "!text!"
echo ----
echo Content of result is "!result!"
--- Output ---
Content of text is "This contains a
newline"
----
Content of result is "This contains a newline"

Resources