I've refer Passing arrays as parameters in bash, but it failed. Here is my test script(both bash 3.0)
The bash version
GNU bash, version 3.00.16(1)-release (sparc-sun-solaris2.10)
Copyright (C) 2004 Free Software Foundation, Inc.
The script t.sh
fn() {
local i
local v1="$1"
local v2="$2"
local v3="$3"
echo "v1=$1"
echo "v2=$2"
echo "v3=$3"
declare -a a1=("${!1}")
declare -a a2=("${!2}")
echo "a1:"
for i in ${!a1[*]} ; do
echo " ${a1[$i]}"
done
echo "a2:"
for i in ${!a2[*]} ; do
echo " ${a2[$i]}"
done
}
caller() {
local a=("a1 a2" "a3" "a4")
local b=("b1" "b2" "b3" "b4")
echo "method 1:"
fn "${a[#]}" "${b[#]}" $1 $2 $3
echo "method 2:" # workable on bash 4.2.45
fn a[#] b[#] $1 $2 $3
}
caller c
Output
method 1:
v1=(a1 a2 a3 a4)
v2=(b1 b2 b3 b4)
v3=c
a1:
a2:
method 2:
v1=a[#]
v2=b[#]
v3=c
t.sh: array assign: line 10: syntax error near unexpected token `('
t.sh: array assign: line 10: `(a1 a2 a3 a4)'
expected output
...
a1:
a1 a2
a3
a4
a2:
b1
b2
b3
b4
I'm not sure where you are having the issue, but I've confirmed operation on Bash 3.2.25:
./caller.sh
method 1:
v1=a1 a2
v2=a3
v3=a4
a1:
a2:
method 2:
v1=a[#]
v2=b[#]
v3=c
a1:
a1 a2
a3
a4
a2:
b1
b2
b3
b4
03:36 lakehouse~/scr/tmp> bash --version
GNU bash, version 3.2.25(1)-release (i586-suse-linux-gnu)
Copyright (C) 2005 Free Software Foundation, Inc.
Related
I am trying to create new set of variables using arrays. but i am getting this error "
ERROR: Array subscript out of range at line 581 column 23."
in my program i have set of macro variables n1 to n15
Here is my code i can;t find out how does my arrays goes out of range since all arrays have 15 elements
data allsae1;
*length _a1 _a2 _a3 _a4 _a5 _a6 _a7 _a8 _a9 _a10 _a11 _a12 _a13 _a14 _a99 _b1 _b2 _b3 _b4 _b5 _b6 _b7 _b8 _b9 _b10 _b11 _b12 _b13 _b14 _b99 $10;
set allsae;
array _anum{15} a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a99;
array _bnum{15} b1 b2 b3 b4 b5 b6 b7 b8 b9 b10 b11 b12 b13 b14 b99;
array astat{15} _a1 _a2 _a3 _a4 _a5 _a6 _a7 _a8 _a9 _a10 _a11 _a12 _a13 _a14 _a99;
array bstat{15} _b1 _b2 _b3 _b4 _b5 _b6 _b7 _b8 _b9 _b10 _b11 _b12 _b13 _b14 _b99;
%macro stats;
%do i=1 %to 15;
%if _anum[i] !=. %then %do;
astat[i]=strip(put(_anum[i], best.))||" ("||strip(put(_anum[i]/(&&n&i) *100, 8.1))||"%)";
%end;
bstat[i] = strip(put(_bnum[i], best.));
%end;
%mend stats;
%stats;
run;
Why do you have macro code here? I don't see any place where you need to generate SAS code. The only place is the reference to &&n&i but I don't see where you have defined any macro variables named N1, N2, etc.
The string _anum[i] is always not equal to the string . so you always generate the SAS statement
astat[i]=strip(put(_anum[i], best.))||" ("||strip(put(_anum[i]/(<something>) *100, 8.1))||"%)";
But you never created the variable I so the index into astat and _anum arrays will be invalid.
Most likely you just want a normal DO loop and don't need to define a macro at all. If you really have those 15 macro variables and they contain numeric strings you might just use the SYMGETN() function.
do i=1 to 15;
if not missing(_anum[i]) then do;
astat[i]=strip(put(_anum[i], best.))||" ("||strip(put(_anum[i]/(symgetn(cats('n',i))) *100, 8.1))||"%)";
end;
bstat[i] = strip(put(_bnum[i], best.));
end;
Or just make a temporary array to have those 15 values.
array _n[15] _temporary_ (&n1 &n2 &n3 &n4 .... &n15);
which you then index into with the I variable.
... _n[i] ...
You need arrays not macros here. You're trying to use your macro variables but I would instead suggest you assign your macro variables N to an array. I would also recommend creating a single macro variable not N so you don't have to work about indexes and macro loops.
Create your N using something like this:
proc sql noprint;
select n into N_list_values separated by ", " from yourTable;
quit;
%put &n_list_values;
Then you can use it later on like this in the array.
array _anum{15} a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a99;
array _bnum{15} b1 b2 b3 b4 b5 b6 b7 b8 b9 b10 b11 b12 b13 b14 b99;
array astat{15} _a1 _a2 _a3 _a4 _a5 _a6 _a7 _a8 _a9 _a10 _a11 _a12 _a13 _a14 _a99;
array bstat{15} _b1 _b2 _b3 _b4 _b5 _b6 _b7 _b8 _b9 _b10 _b11 _b12 _b13 _b14 _b99;
array _n(15) _temporary_ (&N_list_values);
do i=1 to 15;
if _anum[i] !=. then do;
astat[i]=strip(put(_anum[i], best.))||" ("||strip(put(_anum[i]/(_n(i)) *100, 8.1))||"%)";
end;
bstat[i] = strip(put(_bnum[i], best.));
end;
New to using Grep. Basically I have two text files; blacklist.txt and many foo.txt in different directories.
I started off using:
grep -vE "(insert|blacklist|items|here)" foo.txt > filtered_foo.txt
but my blacklist has grown exponentially and so I need to compare the two files instead.
In foo.txt there are four columns with columns 1,2,3 being unique. I want to delete rows where column 4 matches a string in my blacklist.
Sample of a foo.txt
A1 A2 A3 Bob
B1 B2 B3 Anne
C1 C2 C3 Henry
D1 D2 D3 Ted
blacklist.txt
Anne
Ted
Desired output: filtered_foo.txt
A1 A2 A3 Bob
C1 C2 C3 Henry
I have tried different things in grep such as:
grep -vF "'cat blacklist.txt'" foo.txt > filtered_foo.txt
Use the -f option to get the patterns from a file.
grep -vF -f blacklist.txt foo.txt > filtered_foo.txt
I just started using shell programming. I want to automatically change directories and then rename some files in there. Here's my problem: The name of the directories are numbered but directories < 10 are zero-padded (01 02...09). How can I define an array using some sort of sequencing without typing each directory name manually?
This is what I've tried so far:
array = (printf "%.2d " {1..8} {11..27} {29..32} {34..50}) ## should say 01 02 03 ..08 11..27 29..32 34..50
for i in "${array[#]}"
do
echo "dir_a/dir_b/sub$i/dir_c/"
done
However, it doesn't work and the result looks like: "subprintf", "sub%.2s", "sub1" etc.
Can you help me there?
In a next step I want to filter certain numbers in the array, e.g. 03, 09, 10, 28, 33 as these directories don't exist. Is there some easy solution to create such an array without concatenating 5 separate arrays?
Many thanks in advance,
Kati
Is there a need to use arrays? Otherwise, for bash 4, you can do
for i in {01..08} {11..27} {29..32} {34..50}; do
echo "dir_a/dir_b/sub${i}/dir_c/"
done
For an older version of bash you have to add the 0 yourself:
for i in 0{1..8} {11..27} {29..32} {34..50}; do
echo "dir_a/dir_b/sub${i}/dir_c/"
done
Of course, if you want to have an array, you can do
array=({01..08} {11..27} {29..32} {34..50})
or
array=(0{1..8} {11..27} {29..32} {34..50})
You could do this:
declare -a dirs=('01' '02' '03' '04' '05' '06' '07' '08')
echo ${dirs[#]}
01 02 03 04 05 06 07 08
# Make up next sequence
declare -a b=`seq 11 18`
echo ${b[#]}
11 12 13 14 15 16 17 18
# Add sequences together
dirs=("${dirs[#]}" ${b})
echo ${dirs[#]}
01 02 03 04 05 06 07 08 11 12 13 14 15 16 17 18
find [0-9][0-9] -type d | while read dirname
do
if [ $(echo "${dirname}" | sed -n '/01/p') ]
then
cd "${dirname}"
mv foo bar
cd ..
fi
done
Then you can just write another elif and sed check for every directory which contains files you want to rename. I know it's not what you asked for, but it is infinitely simpler. If you're allowed to, I'd also strongly recommend renaming that directory tree, as well.
#/bin/bash
raw=({01..08} {11..27} {29..32} {34..50})
filter=(03 09 10 28 33)
is_in() {
for e in "${#:2}"; do [[ "$e" == "$1" ]] && return 0; done
return 1
}
for i in ${raw[#]}; do
is_in $i ${filter[#]} || echo "dir_a/dir_b/sub$i/dir_c"
done
It'll take the numbers in the raw array and exclude every occurance of the ones in the filter array.
Is there a way to loop over multiple lists in parallel in a makefile or CMake file?
I would like to do something like the following in CMake, except AFAICT this syntax isn't supported:
set(a_values a0 a1 a2)
set(b_values b0 b1 b2)
foreach(a in a_values b in b_values)
do_something_with(a b)
endforeach(a b)
This would execute:
do_something_with(a0 b0)
do_something_with(a1 b1)
do_something_with(a2 b2)
I would accept an answer in either CMake or Make, though CMake would be preferred. Thanks!
Here you go:
set(list1 1 2 3 4 5)
set(list2 6 7 8 9 0)
list(LENGTH list1 len1)
math(EXPR len2 "${len1} - 1")
foreach(val RANGE ${len2})
list(GET list1 ${val} val1)
list(GET list2 ${val} val2)
message(STATUS "${val1} ${val2}")
endforeach()
As of CMake 3.17, the foreach() loop supports a ZIP_LISTS option to iterate through two (or more) lists simultaneously:
set(a_values a0 a1 a2)
set(b_values b0 b1 b2)
foreach(a b IN ZIP_LISTS a_values b_values)
message("${a} ${b}")
endforeach()
This prints:
a0 b0
a1 b1
a2 b2
In make you can use the GNUmake table toolkit to achieve this by handling the two lists as 1-column tables:
include gmtt/gmtt.mk
# declare the lists as tables with one column
list_a := 1 a0 a1 a2 a3 a4 a5
list_b := 1 b0 b1 b2
# note: list_b is shorter and will be filled up with a default value
joined_list := $(call join-tbl,$(list_a),$(list_b), /*nil*/)
$(info $(joined_list))
# Apply a function (simply output "<tuple(,)>") on each table row, i.e. tuple
$(info $(call map-tbl,$(joined_list),<tuple($$1,$$2)>))
Output:
2 a0 b0 a1 b1 a2 b2 a3 /*nil*/ a4 /*nil*/ a5 /*nil*/
<tuple(a0,b0)><tuple(a1,b1)><tuple(a2,b2)><tuple(a3,/*nil*/)><tuple(a4,/*nil*/)><tuple(a5,/*nil*/)>
There are two files
first.file
M1
M2
M3
...
second.file
A1 M1
A2 M1
A2 M3
A3 M2
A3 M4
A3 M5
....
I want to match first.file to second.file My result file should be like that:
result.file
A1 M1
A2 M1
A2 M3
A3 M2
How can I do that with awk codes ?
Thank you in advance
awk '
BEGIN { while (getline < "first.file") { file1[$0]=1 } }
$2 in file1 { print }
' <second.file
Use the below:
grep -f firstfile secondfile
grep is enough.
even though we can do this with awk too,i prefer grep
If you still insist on awk,Then i have a very simple solution in awk too.
awk 'FNR==NR{a[$0];next}($0 in a)' file2 file1
Explanation:
Put file2 entries into an array. Then iterate file1, each time finding those entries in the array.