Csh adding strings to an array, whitespace troubles - arrays

I’m having trouble doing something basic with csh. I have a string:
set newCmd = "$expansionCmd –option1 –option2 …"
And I’m creating an array of these strings, which I later want to execute:
set expansionCmdList = ($expansionCmdList[*] "$newCmd")
#I also tried without quotes, e.g. just $newCmd
Finally I try to iterate over and execute these commands:
foreach exCmd ($expansionCmdList)
`exCmd` #execute it in the shell
end
However the problem is that the array entries are not the full string, but every part of the string separated by whitespace, i.e. the first entry is just “$expansionCmd”, the next entry would be “—option1” etc.
Apologies in advance for using c shell, my company's code base is stuck with it.

Any time you are expanding an entire array and want to keep its individual elements' identities intact, you need the :q (for "quoted") modifier on the expansion. Otherwise, as soon as you do something like set expansionCmdList=($expansionCmdList[*] "$newCmd"), all previous commands in the list are split out into their component words, each of which is now its own array element. Simple demonstration:
% set a = ( a "b c" d )
% echo $a[2]
b c
% set a = ( $a[*] e )
% echo $a[2]
b
Oops, you've messed up the array before you even get to your execution loop. Things go much better with :q:
% set a = ( a "b c" d )
% set a = ( $a:q e )
% echo $a[2]
b c
You need to use the same modifier in the for loop:
foreach exCmd ($expansionCmdList:q)
Finally, `exCmd` tries to run a command literally named "exCmd", and then take its output and run that as a command. What you probably want to do is simply execute a command equal to the value of the variable. You will likely run into more whitespace woes here, and you can't solve them by making each command an array since csh doesn't support arrays of arrays. Fair warning. But if the commands don't have any quotation needs, this will work:
$exCmd

Mark's solution is clearly superior for most applications, but there is another option. Instead of using foreach directly, get the size of the array and iterate through the sequence:
set BUILD_MATRIX = ( "makefile.make:make --jobs --makefile=makefile.make" \
"Makefile:make --jobs --makefile=Makefile" \
"build.xml:ant" )
foreach i ( `seq ${#BUILD_MATRIX}` )
echo $i
echo $BUILD_MATRIX[$i]
end
(copied from Accessing array elements with spaces in TCSH)

Related

Group similar element of array together to use in foreach at once in perl

i have an array which contents elements in which some elements are similiar under certain conditions (if we detete the "n and p" from the array element then the similiar element can be recognised) . I want to use these similiar element at once while using foreach statement. The array is seen below
my #array = qw(abc_n abc_p gg_n gg_p munday_n_xy munday_p_xy soc_n soc_p);
Order of the array element need not to be in this way always.
i am editing this question again. Sorry if i am not able to deliver the question properly. I have to print a string multiple times in the file with the variable present in the above array . I am just trying to make you understand the question through below code, the below code is not right in any sense .... i m just using it to make you understand my question.
open (FILE, ">" , "test.v");
foreach my $xy (#array){
print FILE "DUF A1 (.pin1($1), .pin2($2));" ; // $1 And $2 is just used to explain that
} // i just want to print abc_n and abc_p in one iteration of foreach loop and followed by other pairs in successive loops respectively
close (FILE);
The result i want to print is as follows:
DUF A1 ( .pin1(abc_n), .pin2(abc_p));
DUF A1 ( .pin1(gg_n), .pin2(gg_p));
DUF A1 ( .pin1(munday_n_xy), .pin2(munday_p_xy));
DUF A1 ( .pin1(soc_n), .pin2(soc_p));
The scripting language used is perl . Your help is really appreciated .
Thank You.!!
Partitioning a data set depends entirely on how data are "similiar under certain conditions."
The condition given is that with removal of _n and _p the "similar" elements become equal (I assume that underscore; the OP says n and p). In such a case one can do
use warnings;
use strict;
use feature 'say';
my #data = qw(abc_n abc_p gg_n gg_p munday_n_xy munday_p_xy soc_n soc_p);
my %part;
for my $elem (#data) {
push #{ $part{ $elem =~ s/_(?:n|p)//r } }, $elem;
}
say "$_ => #{$part{$_}}" for keys %part;
The grouped "similar" strings are printed as a demo since I don't understand the logic of the shown output. Please build your output strings as desired.
If this is it and there'll be no more input to process later in code, nor will there be a need to refer to those common factors, then you may want the groups in an array
my #groups = values %part;
If needed throw in a suitable sorting when writing the array, sort { ... } values %part.
For more fluid and less determined "similarity" try "fuzzy matching;" here is one example.

Why is Perl giving "Can't modify string in scalar output" error?

I'm pretty new to Perl and this is my most complex project yet. Apologies if any parts of my explanation don't make sense or I miss something out - I'll be happy to provide further clarification. It's only one line of code that's causing me an issue.
The Aim:
I have a text file that contains a single column of data. It reads like this:
0
a,a,b,a
b,b,b,a
1
a,b,b,a
b,b,b,a
It continues like this with a number in ascending order up to 15, and the following two lines after each number are a combination of four a's or b's separated by commas. I have tied this file to an array #diplo so I can specify specific lines of it.
I also have got a file that contains two columns of data with headers that I have converted into a hash of arrays (with each of the two columns being an array). The name of the hash is $lookup and the array names are the names of the headings. The actual arrays only start from the first value in each column that isn't a heading. This file looks like this:
haplo frequency
"|5,a,b,a,a|" 0.202493719
"|2,b,b,b,a|" 0.161139191
"|3,b,b,b,a|" 0.132602458
This file contains all of the possible combinations of a or b at the four positions combined with all numbers 0-14 and their associated frequencies. In other words, it includes all possible combinations from "|0,a,a,a,a|" followed be "|1,a,a,a,a|" through to "|13,b,b,b,b|" and "|14,b,b,b,b|".
I want my Perl code to go through each of the combinations of letters in #diplo starting with a,a,b,a and record the frequency associated with the row of the haplo array containing each number from 0-14, e.g. first recording the frequency associated with "|0,a,a,b,a|" then "|1,a,a,b,a|" etc.
The output would hopefully look like this:
0 #this is the number in the #diplo file and they increase in order from 0 up to 15
0.011 0.0023 0.003 0.0532 0.163 0.3421 0.128 0.0972 0.0869 0.05514 0.0219 0.0172 0.00824 0.00886 0.00196 #these are the frequencies associated with x,a,a,b,a where x is any number from 0 to 14.
My code:
And here is the Perl code I created to hopefully sort this out (there is more to create the arrays and such which I can post if required, but I didn't want to post a load of code if it isn't necessary):
my $irow = 1; #this is the row/element number in #diplo
my $lrow = 0; #this is the row/element in $lookup{'haplo'}
my $copynumber = 0;
#print "$copynumber, $diplo[2]";
while ($irow < $diplolines - 1) {
while ($copynumber < 15) {
while ($lrow < $uplines - 1) {
if ("|$copynumber,$diplo[$irow]|" = $lookup{'haplo'}[$lrow]) { ##this is the only line that causes errors
if ($copynumber == 0) {
print "$diplo[$irow-1]\n";
#print "$lookup{'frequency'}[$lrow]\t";
}
print "$lookup{'frequency'}[$lrow]\t";
}
$lrow = $lrow + 1;
}
$lrow = 0;
$copynumber = $copynumber + 1;
}
$lrow = 0;
$copynumber = 0;
$irow = $irow + 1;
}
However, the line if ("|$copynumber,$diplo[$irow]|" = $lookup{'haplo'}[$lrow]) is causing an error Can't modify string in scalar assignment near "]) ".
I have tried adding in speech marks, rounded brackets and apostrophes around various elements in this line but I still get some sort of variant on this error. I'm not sure how to get around this error.
Apologies for the long question, any help would be appreciated.
EDIT: Thanks for the suggestions regarding eq, it gets rid of the error and I now know a bit more about Perl than I did. However, even though I don't get an error now, if I put anything inside the if loop for this line, e.g. printing a number, it doesn't get executed. If I put the same command within the while loop but outside of the if, it does get executed. I have strict and warnings on. Any ideas?
= is assignment, == is numerical comparison, eq is string comparison.
You can't modify a string:
$ perl -e 'use strict; use warnings; my $foo="def";
if ("abc$foo" = "abcdef") { print "match\n"; } '
Found = in conditional, should be == at -e line 1.
Can't modify string in scalar assignment at -e line 1, near ""abcdef") "
Execution of -e aborted due to compilation errors.
Nonnumerical strings act like zeroes in a numerical comparison:
$ perl -e 'use strict; use warnings; my $foo="def";
if ("abc$foo" == 0) { print "match\n"; } '
Argument "abcdef" isn't numeric in numeric eq (==) at -e line 1.
match
A string comparison is probably what you want:
$ perl -e 'use strict; use warnings; my $foo="def";
if ("abc$foo" eq "abcdef") { print "match\n"; } '
match
This is the problematic expression:
"|$copynumber,$diplo[$irow]|" = $lookup{'haplo'}[$lrow]
The equals sign (=) is an assignment operator. It assigns the value on its right-hand side to the variable on its left-hand side. Therefore, the left-hand operand needs to be a variable, not a string as you have here.
I don't think you want to do an assignment here at all. I think you're trying to check for equality. So don't use an assignment operator, use a comparison operator.
Perl has two equality comparison operators. == does a numeric comparison to see if its operands are equal and eq does a string comparison. Why does Perl need two operators? Well Perl converts automatically between strings and numbers so it can't possibly know what kind of comparison you want to do. So you need to tell it.
What's the difference between the two types of comparison? Well, consider this code.
$x = '0';
$y = '0.0';
Are $x and $y equal? Well it depends on the kind of comparison you do. If you compare them as numbers then, yes, they are the same value (zero is the same thing whether it's an integer or a real number). But if you compare them as strings, they are different (they're not the same length for a start).
So we now know the following
$x == $y # this is true as it's a numeric comparison
$x eq $y # this is false as it's a string comparison
So let's go back to your code:
"|$copynumber,$diplo[$irow]|" = $lookup{'haplo'}[$lrow]
I guess you started with == here.
"|$copynumber,$diplo[$irow]|" == $lookup{'haplo'}[$lrow]
But that's not right as |$copynumber,$diplo[$irow]| is clearly as string, not a number. And Perl will give you a warning if you try to do a numeric comparison using a value that doesn't look like a number.
So you changed it to = and that doesn't work either as you've now changed it to an assignment.
What you really need is a string comparison:
"|$copynumber,$diplo[$irow]|" eq $lookup{'haplo'}[$lrow]

how to use RANDOM command in declared array

I am trying to build a script where I need to create a password generator with the following parameters :
must be at least 8 characters long but not longer than 16 characters.
must contain at least 1 digit (0-9).
must contain at least 1 lowercase letter.
must contain at least 1 uppercase letter.
must contain exactly one and only one of # # $ % & * + - =
I have two ideas :
The first :
#!/bin/bash
#
#Password Generator
#
#
Upper=('A''B''C''D''E''F''G''H''I''J'K''L''M''N''O''P''Q''R''S''T''U''V''W''X''Y''Z')
Lower=('a''b''c''d''e''z''f''g''h''i''j''k''l''m''o'''p''q''r''s''t''u''v''w''x''y''z')
Numbers=('1''2''3''4''5''6''7''8''9')
SpecialChar=('#''#''$''%''&''*''+''-''=')
if [ S# -eq 0 ] ; then
Pwlength=`shuf -i 8-16 -n 1`
Password=`< /dev/urandom tr -dc A-Za-z0-9$SpecialChar | head -c $Pwlength`
echo "Random Password is being generated for you"
sleep 5
echo "Your new password is : $Password"
exit
The problem is I get characters that I didnt even defined ?
The secound idea :
for((i=0;i<4;i++))
do
password=${Upper[$random % ${#Lower[#]} ] }
password=${Upper[$random % ${#Upper[#]} ] }
password=${Upper[$random % ${#Number[#]} ] }
password=${Upper[$random % ${#SpecialChar[#]} ] }
done
For some reason non of them work ;/
In your first example, move the "-" character to the end of the SpecialChar. I think the definition as you had it results in allowing "+" to "=" (i.e., the value passed to tr reads as "+-="), which accounts for the characters you were not expecting. Alternatively, replace the "-" with "_".
So, try a definition like:
SpecialChar=('#''#''$''%''&''*''+''=''-')
As already mentioned, it would be cleaner and easier to use directly a string to handle list of characters. Handling special characters in an array may cause side effects (for instance getting the list of files in the current directory with the '*' character). In addition, arrays may be difficult to pass as function arguments).
ALPHA_LOW="abcdefghijklmnopqrstuvwxyz"
# Then simply access the char in the string at the ith position
CHAR=${ALPHA_LOW:$i:1}
You could generate upper cases from the lower cases.
ALPHA_UP="`echo \"$ALPHA_LOW\" | tr '[:lower:]' '[:upper:]'`"
The variable which contains a random number is $RANDOM (in capital letters).
sleep 5 is unnecessary.
You need to find a way to keep count of occurrences left for each character type. For more information, I wrote a complete script to do what you described here.
Your first attempt has the following problems:
You are using arrays to contain single strings. Pasting 'A''B''C' is equivalent to 'ABC'. Simply converting these to scalar variables would probably be the simplest fix.
You are not quoting your variables.
You declare Upper, Lower, and Numbers, but then never use them.
Using variables for stuff you only ever use once (or less :-) is dubious anyway.
As noted by #KevinO, the dash has a special meaning in tr, and needs to be first or last if you want to match it literally.
On top of that, the sleep 5 doesn't seem to serve any useful purpose, and will begin to annoy you if it doesn't already. If you genuinely want a slower computer, I'm sure there are people here who are willing to trade.
Your second attempt has the following problems:
The special variable $RANDOM in Bash is spelled in all upper case.
You are trying to do modulo arithmetic on (unquoted!) arrays of characters, which isn't well-defined. The divisor after % needs to be a single integer. You can convert character codes to integers but it's sort of painful, and it's less than clear what you hope for the result to be.
A quick attempt at fixing the first attempt would be
Password=$(< /dev/urandom tr -dc 'A-Za-z0-9##$%&*+=-' | head -c $(shuf -i 8-16 -n 1))
If you want to verify some properties on the generated password, I still don't see why you would need arrays.
while read -r category expression; do
case $Password in
*[$expression]*) continue;;
*) echo "No $category"; break;;
esac
done <<'____HERE'
lowercase a-z
uppercase A-Z
numbers 0-9
specials -##$%&*+=
HERE

For loop to take the value of the whole array each time

Suppose I have 3 arrays, A, B and C
I want to do the following:
A=("1" "2")
B=("3" "4")
C=("5" "6")
for i in $A $B $C; do
echo ${i[0]} ${i[1]}
#process data etc
done
So, basically i takes the value of the whole array each time and I am able to access the specific data stored in each array.
On the 1st loop, i should take the value of the 1st array, A, on the 2nd loop the value of array B etc.
The above code just iterates with i taking the value of the first element of each array, which clearly isn't what I want to achieve.
So the code only outputs 1, 3 and 5.
You can do this in a fully safe and supportable way, but only in bash 4.3 (which adds namevar support), a feature ported over from ksh:
for array_name in A B C; do
declare -n current_array=$array_name
echo "${current_array[0]}" "${current_array[1]}"
done
That said, there's hackery available elsewhere. For instance, you can use eval (allowing a malicious variable name to execute arbitrary code, but otherwise safe):
for array_name in A B C; do
eval 'current_array=( "${'"$array_name"'[#]}"'
echo "${current_array[0]}" "${current_array[1]}"
done
If the elements of the arrays don't contain spaces or wildcard characters, as in your question, you can do:
for i in "${A[*]}" "${B[*]}" "${C[*]}"
do
iarray=($i)
echo ${iarray[0]} ${iarray[1]}
# process data etc
done
"${A[*]}" expands to a single string containing all the elements of ${A[*]}. Then iarray=($i) splits this on whitespace, turning the string back into an array.

accessing array result in procedure using tcl

am beginner in Tcl/tk, facing problem in accessing array in procedure
below is my problem statement
proc myproc {args} {
set aaa ""
set bbb ""
set ccc ""
foreach "field value" $args {
set $field $value
}
# assigning input args values to an array a
set a(3:0) $aaa
set a(6:4) $bbb
set a(25:7) $ccc
#do some computation on input arguments may be addition
#
#
#
# now the result am trying to fetch into another array b
set $b(word0) $x
set $b(word1) $y
set $b(word2) $z
set $b(word3) $u
return [array get b]
}
now i need to pass arguments to myproc and return array i need to access.
set args_1 "g 1 h 4 k 6"
i tried the below syntax it was throwing me error.
array set a [myproc[array get $args_1]]
can somebody help me in solving this issue
trying to give string as input for procedure myproc
and later trying to do some computation with that input values.
later after all computation got set of string values, which are assigned to array as below
set $b(word0) $x
set $b(word1) $y
set $b(word2) $z
set $b(word3) $u
want to send this array b as return.
example:
proc myproc {} {
set $b(word0) $x
set $b(word1) $y
set $b(word2) $z
set $b(word3) $u
return [array get b]
}
i have tried to access array b as below
array set a [myproc[array get b]]
it worked :) was able to creat new array in calling function.
but, need to pass string arguments to myproc and get return as array
That function looks OK to me. There may be better ways to write it but it's essentially OK as is.
There are however a couple of problems with how you call that function.
First, you're confusing arrays and lists. In tcl, an array is a collection of key-value pairs. Other languages call this a "hash" or "map". A list is what is sounds like: a list of values. Other languages call this "array" or "list".
So, first off:
tcl other languages
--- ---------------
array = hash
list = array
The name "array" was chosen because the concept of a collection of key-value pairs is known in computer science as "associative arrays". Which is a term that predates the use of the word "array" to mean a list of values in languages like C and Java.
So, here you're declaring a list:
set args_1 "g 1 h 4 k 6"
And you're trying to access it as an array:
array get $args_1
Which should throw out an error that says that $args_1 is not an array. Which is in fact true.
So, simply replace it with the list variable:
$args_1
Which gives us:
array set a [myproc$args_1]
This should throw another error which basically says that the function myproc g 1 h 4 k 6 doesn't exist. Yes, in tcl it is valid to have whitespace in a function's name. For example:
proc "an example" {} {return "an example"}
That's valid code. And you call it like this:
set x ["an example"]
So it's no surprise that tcl can't find a function called "myproc g 1 h 4 k 6".
What this means is that whitespace is significant in tcl. You can't do:
set x [y[z]]
That's most likely a syntax error. It should be:
set x [y [z]]
# ^
# |______ whitespace was missing
So your code should be:
array set a [myproc $args_1]

Resources