php strips [[:char_class:]] from the string - modx

When concatenating mysql regex character classes in php they disappear from the resulting string i.e.:
$regexp_arr = array('(word1)', '(word2)');
$value = 'word3';
$regexp_str = implode('[[:space:]]', $regexp_arr);
$v1 = '[[:<:]](' . $value . ')';
echo $regexp_str;
// gives
'(word1)(word2)';
// instead of
'(word1)[[:space:]](word2)'
echo $v1;
// gives
'(word3)'
//instead of
'[[:<:]](word3)'
I've tried with double quotation marks ", the result still the same.
Is there a special way to concatenate this in php? Why are the '[[:char_class:]]' getting stripped?
server php version is 5.6.36

In MODX, [[ and ]] are special characters used to indicate they are tags MODX needs to process. Even when you echo or retrieve it from the database, MODX will process them when rendering.
For debugging, you can follow-up your echo with an exit().
echo $regexp_str;
exit();
That short-circuits MODX and gives you the actual value of the string including the square brackets.
If you want the value to be visible in a MODX-rendered resource or template, then you'll have to replace them with their html entities first:
$regexp_str = str_replace(['[',']'], ['[', ']'], $regexp_str);

Related

How do you compare the value of an array to a variable in bash script?

I'm practicing bash and honestly, it is pretty fun. However, I'm trying to write a program that compares an array's value to a variable and if they are the same then it should print the array's value with an asterisk to the left of it.
#!/bin/bash
color[0]=red
color[1]=blue
color[2]=black
color[3]=brown
color[4]=yellow
favorite="black"
for i in {0..4};do echo ${color[$i]};
if {"$favorite"=$color[i]}; then
echo"* $color[i]"
done
output should be *black
There's few incorrect statements in your code that prevent it from doing what you ask it to. The comparison in bash is done withing square brackets, leaving space around them. You correctly use the = for string comparison, but should enclose in " the string variable. Also, while you correctly address the element array in the echo statement, you don't do so inside the comparison, where it should read ${color[$i]} as well. Same error in the asterisk print. So, here a reworked code with the fixes, but read more below.
#!/bin/bash
color[0]=red
color[1]=blue
color[2]=black
color[3]=brown
color[4]=yellow
favorite=black
for i in {0..4};do
echo ${color[$i]};
if [ "$favorite" = "${color[$i]}" ]; then
echo "* ${color[$i]}"
fi
done
While that code works now, few things that probably I like and would suggest (open to more expert input of course by the SO community): always enclose strings in ", as it makes evident it is a string variable; when looping an array, no need to use index variables; enclose variables always within ${}.
So my version of the same code would be:
#!/bin/bash
color=("red" "blue" "black" "brown" "yellow")
favorite="black"
for item in ${color[#]}; do
echo ${item}
if [ "${item}" = "${favorite}" ]; then
echo "* $item"
fi
done
And a pointer to the great Advanced Bash-Scripting Guide here: http://tldp.org/LDP/abs/html/

How to extract key value pairs from a file when values span multiple lines?

I'm a few weeks into bash scripting and I haven't advanced enough yet to get my head wrapped around this problem. Any help would be appreciated!
I have a "script.conf" file that contains the following:
key1=value1
key2=${HOME}/Folder
key3=( "k3v1" "k3 v2" "k3v3")
key4=( "k4v1"
"k4 v2"
"k4v3"
)
key5=value5
#key6="Do Not Include Me"
In a bash script, I want to read the contents of this script.conf file into an array. I've learned how to handle the scenarios for keys 1, 2, 3, and 5, but the key4 scenario throws a wrench into it with it spanning across multiple lines.
I've been exploring the use of sed -n '/=\s*[(]/,/[)]/{/' which does capture key4 and its value, but I can't figure out how to mix this so that the other keys are also captured in the matches. The range syntax is also new to me, so I haven't figured out how to separate the key/value. I feel like there is an easy regex that would accomplish what I want... in plain-text: "find and group the pattern ^(.*)= (for the key), then group everything after the '=' char until another ^(.*)= match is found, rinse and repeat". I guess if I do this, I need to change the while read line to not handle the key/value separation for me (I'll be looking into this while I'm waiting for a response). BTW, I think a solution where the value of key4 is flattened (new lines removed) would be acceptable; I know for key3 I have to store the value as a string and then convert it to an array later when I want to iterate over it since an array element apparently can't contain a list.
Am I on the right path with sed or is this a job for awk or some other tool? (I haven't ventured into awk yet). Is there an easier approach that I'm missing because I'm too deep into the forest (like changing the while read line in the LoadConfigFile function)?
Here is the code that I have so far in script.sh for processing and capturing the other pairs into the $config array:
__AppDir=$(dirname $0)
__AppName=${__ScriptName%.*}
typeset -A config #init config array
config=( #Setting Default Config values
[key1]="defaultValue1"
[key2]="${HOME}/defaultFolder"
[QuietMode]=0
[Verbose]=0 #Ex. Usage: [[ "${config[Verbose]}" -gt 0 ]] && echo ">>>Debug print"
)
function LoadConfigFile() {
local cfgFile="${1}"
shopt -s extglob #Needed to remove trailing spaces
if [ -f ${cfgFile} ]; then
while IFS='=' read -r key value; do
if [[ "${key:0:1}" == "#" ]]; then
#echo "Skipping Comment line: ${key}"
elif [ "${key:-EMPTY}" != "EMPTY" ]; then
value="${value%%\#*}" # Delete in-line, right comments
value="${value%%*( )}" # Delete trailing spaces
value="${value%%( )*}" # Delete leading spaces
#value="${value%\"*}" # Delete opening string quotes
#value="${value#\"*}" # Delete closing string quotes
#Manipulate any variables included in the value so that they can be expanded correctly
# - value must be stored in the format: "${var1}". `backticks`, "$var2", and "doubleQuotes" are left as is
value="${value//\"/\\\"}" # Escape double quotes for eval
value="${value//\`/\\\`}" # Escape backticks for eval
value="${value//\$/\\\$}" # Escape ALL '$' for eval
value="${value//\\\${/\${}" # Undo the protection of '$' if it was followed by a '{'
value=$(eval "printf '%s\n' \"${value}\"")
config[${key}]=${value} #Store the value into the config array at the specified key
echo " >>>DBG: Key = ${key}, Value = ${value}"
#else
# echo "Skipped Empty Key"
fi
done < "${cfgFile}"
fi
}
CONFIG_FILE=${__AppDir}/${__AppName}.conf
echo "Config File # ${CONFIG_FILE}"
LoadConfigFile ${CONFIG_FILE}
#Print elements of $config
echo "Script Config Values:"
echo "----------------------------"
for key in "${!config[#]}"; do #The '!' char gets an array of the keys, without it, we would get an array of the values
printf " %-20s = %s\n" "${key}" "${config[${key}]}"
done
echo "------ End Script Config ------"
#To convert to an array...
declare -a valAsArray=${config[RequiredAppPackages]} #Convert the value from a string to an array
echo "Count = ${#valAsArray[#]}"
for itemCfg in "${valAsArray[#]}"; do
echo " item = ${itemCfg}"
done
As I mentioned before, I'm just starting to learn bash and Linux scripting in general, so if you see that I'm doing some taboo things in other areas of my code too, please feel free to provide feedback in the comments... I don't want to start bad habits early on :-).
*If it matters, the OS is Ubuntu 14.04.
EDIT:
As requested, after reading the script.conf file, I would like for the elements in $config[#] to be equivalent to the following:
typeset -A config #init config array
config=(
[key1]="value1"
[key2]="${HOME}/Folder"
[key3]="( \"k3v1\" \"k3 v2\" \"k3v3\" )"
[key4]="( \"k4v1\" \"k4 v2\" \"k4v3\" )"
[key5]="value5"
)
I want to be able to convert the values of elements 'key4' and 'key3' into an array and iterated over them the same way in the following code:
declare -a keyValAsArray=${config[keyN]} #Convert the value from a string to an array
echo "Count = ${#keyValAsArray[#]}"
for item in "${keyValAsArray[#]}"; do
echo " item = ${item}"
done
I don't think it matters if \n is preserved for key4's value or not... that depends on if declare has a problem with it.
A shell is an environment from which to call tools with a language to sequence those calls. It is NOT a tool to manipulate text. The standard UNIX tool to manipulate text is awk. Trying to manipulate text in shell IS a bad habit, see why-is-using-a-shell-loop-to-process-text-considered-bad-pr‌​actice for SOME of the reasons why
You still didn't post the expected result of populating the config array so I'm not sure but I think this is what you wanted:
$ cat tst.sh
declare -A config="( $(awk '
{ gsub(/^[[:space:]]+|([[:space:]]+|#.*)$/,"") }
!NF { next }
/^[^="]+=/ {
name = gensub(/=.*/,"",1)
value = gensub(/^[^=]+=/,"",1)
n2v[name] = value
next
}
{ n2v[name] = n2v[name] OFS $0 }
END {
for (name in n2v) {
value = gensub(/"/,"\\\\&","g",n2v[name])
printf "[%s]=\"%s\"\n", name, value
}
}
' script.conf
) )"
declare -p config
$ ./tst.sh
declare -A config='([key5]="value5" [key4]="( \"k4v1\" \"k4 v2\" \"k4v3\" )" [key3]="( \"k3v1\" \"k3 v2\" \"k3v3\")" [key2]="/home/Ed/Folder" [key1]="value1" )'
The above uses GNU awk for gensub(), with other awks you'd use [g]sub() instead.

bash script function scope

function generateFileList {
for entry in "$ORIGINATION_PATH"/*
do
entry=${entry%.*} # retain the part before the dot
entry=${entry##*/} # retain the part after the last slash
if [ $(contains "${FILENAME[#]}" $entry) == "n" ]; then
FILENAME[$fn_counter]=$entry
fn_counter=(expr $fn_counter + 1)
echo $entry "added to filelist"
echo ${FILENAME[$fn_counter]}
fi
done
NUMBER_OF_FILES=$(expr ${#FILENAME[#]} + 1)}
I have this function .My $ORIGINATION_PATH has many files in it. However, when I call this function my $FILENAME array gets populated only with one entry.Why? Inside the function everything seems fine, and it seems that $FILENAME array gets all the values it needs to get, but when I check outside the function I only get one value in the $FILENAME aray
Problems with your code and suggestions for improvement:
You should initialize ${FILENAME[#]} to an empty array (either in the function itself if you always want the function to generate a new list of files from scratch, or before calling the function if you want to be able to build up a composite list of files by calling the function repeatedly on different base directories).
You should initialize $fn_counter to zero before starting the loop. Or, for the composite build-up idea, to the number of elements currently in ${FILENAME[#]}. Actually, another, perhaps preferable solution, would be to remove the $fn_counter variable entirely and replace it with ${#FILENAME[#]}, since it should always be equal to that value.
In the line fn_counter=(expr $fn_counter + 1), you're assigning $fn_counter to an array, rather than incrementing it. This is because you forgot the dollar before the open parenthesis. If you ran fn_counter=$(expr $fn_counter + 1) then it would work. But there's a better way to increment a numeric variable: let ++fn_counter.
You don't have to dollar-prefix variables in arithmetic expressions. So, for example, we can say ${FILENAME[fn_counter]} instead of ${FILENAME[$fn_counter]}.
You're trying to echo the element of ${FILENAME[#]} that was just added in the current iteration, but indexing it with $fn_counter after it was incremented, which is incorrect. You can solve this by subtracting 1 from it, i.e. echo "${FILENAME[fn_counter-1]}". Or, if removing $fn_counter, echo "${FILENAME[${#FILENAME[#]}-1]}".
When assigning $NUMBER_OF_FILES, I don't know why you're adding 1 to ${#FILENAME[#]}. The number of elements in the ${FILENAME[#]} array should be equal to the number of files, without requiring an increment, no? I recommend removing this variable entirely, since the value can be accessed directly as ${#FILENAME[#]}.
I recommend you pass inputs as arguments (e.g. pass $ORIGINATION_PATH as an argument) and use the local keyword to reduce the likelihood of variable clashes between functions. Globals are the default in bash, which creates dangerous possibilities for different functions to step on each others' toes. For example, imagine if the contains function (assuming it's a shell function) assigned a value to the global $entry variable.
I recommend always using the [[ command rather than [, as it's more powerful, and it's good to be consistent.
As written, your script won't work correctly on an empty directory. You could test in advance if the directory is empty (e.g. [[ -n "$(find "$ORIGINATION_PATH" -maxdepth 0 -empty)" ]]). Another solution is to set nullglob. Another solution is to skip glob words that don't actually exist (e.g. if [[ ! -e "$entry" ]]; then continue; fi;).
Always double-quote variable expansions to protect against word splitting, which takes place after variable expansion. For example, the contains call should be contains "${FILENAME[#]}" "$entry" (notice the double-quoting around $entry). The only exceptions are (1) when assigning a string variable to a string variable, i.e. new=$old, in which case you don't have to quote it, and (2) when expanding a numeric variable, which is guaranteed not to be corrupted by word splitting.
Here's a working solution, filling in the missing pieces:
function contains {
local target="${#:$#:1}";
local -a array=("${#:1:$#-1}");
local elem='';
for elem in "${array[#]}"; do
if [[ "$elem" == "$target" ]]; then
echo 'y';
return;
fi;
done;
echo 'n';
} ## end contains()
function generateFileList {
local path="$1";
local entry='';
for entry in "$path"/*; do
if [[ ! -e "$entry" ]]; then continue; fi;
entry=${entry%.*}; ## retain the part before the dot
entry=${entry##*/}; ## retain the part after the last slash
if [[ "$(contains "${FILENAME[#]}" "$entry")" == 'n' ]]; then
FILENAME[${#FILENAME[#]}]=$entry;
echo "$entry added to filelist";
echo "${FILENAME[${#FILENAME[#]}-1]}";
fi;
done;
} ## end generateFileList()
ORIGINATION_PATH='...';
FILENAME=(); ## build up result on global ${FILENAME[#]} var
generateFileList "$ORIGINATION_PATH";
echo "\${#FILENAME[#]} == ${#FILENAME[#]}";
echo "\${FILENAME[#]} == (${FILENAME[#]})";

Add to integer within string?

I would like to make a string by incrementing a variable within the string.
eg.
$result = "Result: $amount++";
How can this be achieved?
It can be done using trickery.
$result = "Result: ${\( $amount++ )}";
But why would you want to???
$result = "Result: ".$amount++;
If you want to modify a number in a string, you have to use the e modifier for the s operation. This makes Perl evaluating the replacement as an expression.
#! /usr/bin/perl
$_ = "Result: 1\n";
s/\d+/$&+1/e;
print;
It is documented in the Perl manual.
I take it that you have a string that already contains a 'number' (string of digits), and you want to increment that number within.
You'd have to extract the "number" first, in one way or another, since it is merely a string of chars when inside a string; then increment it and join it all back. I'll take it that it is a string of digits bounded by non-digits
my ($pre, $num, $post) = $str =~ m/(\D*)(\d+)(\D*)/;
$str = $pre . ($num+1) . $post;
This makes a critical assumption that the word contains a string of digits in only one place and no digits elsewhere, since if that were not the case the problem would be ill posed.
Just for the curiousity of it I'd like to add a bit to this. A part of a string can be accessed by substr, and that function can be manipulated as an lvalue (can be assigned to). So, if you were to know the starting position and the length of your "number" (what can be found in various ways) you could cram the above process in one statement, if you must
substr($str, $num_beg, $num_len) = substr($str, $num_beg, $num_len) + 1;
or, equally bad
substr($str, $num_beg, $num_len) = ($str =~ m/(\d+)/)[0] + 1;
Now your starting $str string contains the "number" within it incremented. However, this is plain nasty and I cannot recommend any of it. Finally, you can of course find $num_beg and $num_len on the fly, inside of substr, but that is just too much as the poor string would be processed three times in a single statement. (Also, this changes your $str in place, which your question hints is not what you want.)
Added Regex provide the capability to run code in the replacement part, by using /e modifier.
my $str = "ah20bah";
$str =~ s/(\d+)/$1+1/e;
say $str; # it's 'ah21bah'
See this in perlrequick and in perlop.

How explicitly resolve variables in a perl string?

In my perl script I want to have both versions of $config directory:
my $config='$home/client/config';
and
my $config_resolved="$home/client/config";
But I want to get $config_resolved from $config, i.e. something like this:
my $config_resolved=resolve_vars($config);
How can I do such thing in perl?
From the Perl FAQ (which every Perl programmer should read at least once):
How can I expand variables in text strings?
(contributed by brian d foy)
If you can avoid it, don't, or if you can
use a templating system, such as Text::Template or Template Toolkit,
do that instead. You might even be able to get the job done with
sprintf or printf:
my $string = sprintf 'Say hello to %s and %s', $foo, $bar;
However, for the one-off simple case where I don't want to pull out a
full templating system, I'll use a string that has two Perl scalar
variables in it. In this example, I want to expand $foo and $bar to
their variable's values:
my $foo = 'Fred';
my $bar = 'Barney';
$string = 'Say hello to $foo and $bar';
One way I can do this involves the substitution operator and a double /e flag. The
first /e evaluates $1 on the replacement side and turns it into $foo. The
second /e starts with $foo and replaces it with its value. $foo,
then, turns into 'Fred', and that's finally what's left in the string:
$string =~ s/(\$\w+)/$1/eeg; # 'Say hello to Fred and Barney'
The /e will also silently ignore violations of strict, replacing undefined
variable names with the empty string. Since I'm using the /e flag
(twice even!), I have all of the same security problems I have with
eval in its string form. If there's something odd in $foo, perhaps
something like #{[ system "rm -rf /" ]}, then I could get myself in
trouble.
To get around the security problem, I could also pull the
values from a hash instead of evaluating variable names. Using a
single /e, I can check the hash to ensure the value exists, and if it
doesn't, I can replace the missing value with a marker, in this case
??? to signal that I missed something:
my $string = 'This has $foo and $bar';
my %Replacements = (
foo => 'Fred',
);
# $string =~ s/\$(\w+)/$Replacements{$1}/g;
$string =~ s/\$(\w+)/
exists $Replacements{$1} ? $Replacements{$1} : '???'
/eg;
print $string;
I use eval for this.
So, you must replace all scalars (their names) with their values.
$config = 'stringone';
$boo = '$config/any/string';
$boo =~ s/(\$\w+)/eval($1)/eg;
print $boo;
Because you are using my to declare it as private variable, you might as well use a /ee modifier. This can find variables declared to be in local scope:
$boo =~ s/(\$\w+)/$1/eeg;
This is most tidily and safely done by the double-eval modifier on s///.
In the program below, the first /e evaluates the string $1 to get $home, while the second evaluates $home to get the variable's value HOME.
use strict;
my $home = 'HOME';
my $config = '$home/client/config';
my $config_resolved = resolve_vars($config);
print $config_resolved, "\n";
sub resolve_vars {
(my $str = shift) =~ s/(\$\w+)/$1/eeg;
return $str;
}
output
HOME/client/config

Resources