Two arrays with different length in a loop - arrays

I have two arrays with different length, and I need to use them in the same loop.
This is the code
#!/bin/bash
data=`date +%Y-%m-%d`
data1=`date -d "1 day" +%Y-%m-%d`
cd /home/test/em_real/
#first array (today and tomorrow)
days="$data $data1"
#second array (00 till 23)
hours="00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23"
for value in $hours
do
cp /home/test/em_real/mps_"${days[i++]}"_"$value":00:00 /home/DOMAINS/test_case/
sleep 10
done
Tt fails, doesn't get days.
How can I do it?
#fedorqui If now, inside the bucle, I want to remove the dash (-) of days and do another order, I don't know why it doesn't get the string , the code is the following:
days=("$data" "$data1") #create an array properly
for value in {00..23}; do
for day in "${days[#]}"; do
cp "/path/mps_${day}_${value}:00:00" /another/path/test_case/
d=d01
hourSIMULATION=01
clean= echo ${day} | sed -e 's/-//g'
sed -e 's/<domine>/'$d'/g' -e 's/<data-initial>/'$clean$value'/g' -e 's/<hour-SIMULATION>/'$hourSIMULATION'/g' run_prhours > run_pr
done
done
The string $dayclean is empty when I check inside run_pr, do you know what could be the reason?

You are using days[i++] but no i is defined anywhere. Not sure what you want to do with ${days[i++]} but $days is just a string containing "$data $data1".
You probably want to say days=($data $data1) to create an array.
Also, you can say for hour in {00.23} instead of being explicit on the numbers.
Then, you want to loop through the hours and then through the days. For this, use a nested loop:
days=("$data" "$data1") #create an array properly
for value in {00..23}; do
for day in "${days[#]}"; do
cp "/path/mps_${day}_${value}:00:00" /another/path/test_case/
done
done

Related

How to create an array with leading zeros in Bash?

Not very difficult:
#!/bin/bash
hr=(00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23)
for i in ${hr[#]}; do
echo ${hr[i]}
done
But:
user#userver:$ ./stat.sh
00
01
02
03
04
05
06
07
./stat.sh: string 7: 08: value too great for base (error token is "08")
Bash thinks that leading zero means octal number system. What to do?
I think your fundamental problem is that you're using each element of the array to index the array itself.
If you want to just print out the array elements, you should be outputting ${i} rather than ${hr[i]}. The latter is useless in your current code since element zero is 00, element one is 01, and so on.
On the chance that this is a simplified example and you do want to reference a different array based on the content of this one, you have a couple of options:
Realise that the value of an integer and the presentation of it are two distinct things. In other words, use 0, 1, 2, ... but something like printf "%02d" $i if you need to output it (noting this is only ouputting the value in the first array, not the one you're looking up things in).
Exclusively use strings and a string-based associative array rather than an integer-based one, see typeset -A for detail.
Use brace expansion (ranges, repetition).
To create an array:
hours=({00..23})
Loop through it:
for i in "${hours[#]}"; do
echo "$i"
done
Or loop through a brace expansion:
for i in {00..23}; do
echo "$i"
done
Regarding your error, it's because in bash arithmetic, all numbers with leading zeroes are treated as octals, and 08 and 09 are invalid octal numbers. All indexed array subscripts are evaluated as arithmetic expressions. You can fix the problem by using the notation base#number to specify a number system. So for base 10: 10#09, or for i=09, 10#$i. The variable must be prefixed with $, 10#i does not work.
You should be printing your array like this anyway:
Loop through elements:
for i in "${hr[#]}"; do
echo "$i"
done
Loop through indexes:
for i in "${!hr[#]}"; do
echo "index is $i"
echo "element is ${hr[i]}"
done
If you need to do arithmetic on the hours, or any zero padded number, you will lose the zero padding. You can print it again with printf: printf %.2d "$num", where 2 is the minimum width.
When you iterate with:
for i in ${hr[#]}; do
It is iterating the values of the array witch are:
00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23
But when within the loop it has:
echo ${hr[i]}
it is using i as the index of the hr array.
In Bash, an index within the brackets of an array like [i] is an arithmetic context. It means while the value of i=08 the leading 0 within the arithmetic context causes the number to be treated as an octal number, and 8 is an invalid octal number.
If you wanted to iterate your array indexes to process its values by index, then you'd start the loop as:
for i in "${!hr[#]}"; do
This one will perfectly work as it iterates the index into the variable i :
#!/usr/bin/env bash
declare -a hr=(00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23)
for i in "${!hr[#]}"; do
printf '%s\n' "${hr[i]}"
done
Now if all you want is iterate the values of the hr array, just do this way:
#!/usr/bin/env bash
declare -a hr=(00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23)
for e in "${hr[#]}"; do
printf '%s\n' "$e"
done
No need to index the array within the loop, since the elements are already expanded into e.
You can specify it's 10-based :
echo ${hr[10#$i]}

How can I save lines with spaces between characters in the right way (Array)?

Two examples of Lines:
Bob 02 02 10 80 Enquiries
Martin Corp 02 02 10 80 Langar
Note, that the first line doesn't have an information after "Bob", so just spaces.
So my code would be:
$account_name = $inputFileContent[$i].Split(" ",[System.StringSplitOptions]::RemoveEmptyEntries)
Edit:
My output-array should be like:
$account_name =
Bob
(Empty Line)
02
02
10
80
Enquiries
Is there a way to change the code, so I have it saved in a array in that format?
One possibility is to use regular expressions:
Get-Content "data.txt" | ForEach-Object {
$_ -match "^([a-z]+) +(\w*) +((?:\d{2} ){3}\d{2}) +(\w+)$" | Out-Null
$account_name = 1..4 | ForEach-Object {$matches[$_]}
}
This will set $account_name to:
Bob
02 02 10 80
Enquiries
or:
Martin
Corp
02 02 10 80
Langar
Of course, $account_name is over-written each time, so either use it before looping around to the next line, or add each set of items to an array.
Note: I assumed you actually wanted the 'code' as a single item (e.g. '02 02 10 80'), but if not, the pattern can be adjusted to pull out the individual items.
Your input lines contain fixed width as opposite to comma separate values, so you can't use a delimiter (space) to parse the data which may contain empty values. Instead you have to use the position of the values in the string.
Here is an example line, which I have added index numbering above:
012345678901234567890123456789012345678901234567890
Martin Corp 02 02 10 80 Langar
You can notice the first value Martin starts from the second character, index number 1 and is at most 12 characters long. Second value Corp starts from index 13 and is at most 11 characters long, after which first value 02 starts and so on.
We use substring(1,12) to get a 12 characters long slice from the line starting from index 1. To remove (trailing) empty spaces from the string, we call Trim() method.
To create a new object containing data of each line, we use [PSCustomObject] and give names to object properties, or headers if you wish. += operator adds that object to the array.
test.txt
Bob 02 02 10 80 Enquiries
Martin Corp 02 02 10 80 Langar
Script:
$array = #() # Create an empty array
Get-Content .\test.txt | foreach{ # Read test.txt file and handle each line with foreach
$array += [PSCustomObject]#{
Field1 = $_.substring(1,12).Trim();
Field2 = $_.substring(13,11).Trim();
Field3 = $_.substring(25,2).Trim();
Field4 = $_.substring(28,2).Trim();
Field5 = $_.substring(31,2).Trim();
Field6 = $_.substring(34,2).Trim();
Field7 = $_.substring(39); # Substring from index 39 to the end of line
}
}
$array
To output the values each on its own line, you could do it for example by this:
$array | foreach{
$_.Field1
$_.Field2
$_.Field3
$_.Field4
$_.Field5
$_.Field6
$_.Field7
}
ConvertFrom-SourceTable
A while ago, I have created a ConvertFrom-SourceTable cmdlet for reading fixed tables like yours:
$Text = '
Bob 02 02 10 80 Enquiries
Martin Corp 02 02 10 80 Langar'
-Header
As your $Text example has no header you just need to provide it for each column, e.g.:
ConvertFrom-SourceTable -Header Name,Company,A,B,C,D,Comment $Text | Format-Table
Name Company A B C D Comment
---- ------- - - - - -------
Bob 02 02 10 80 Enquiries
Martin Corp 02 02 10 80 Langar
02 02 10 80
You might also define the -Header nas a single string which is than used to not just the column names but also the alignment of the columns. In your example, if 02 02 10 80 is actually a single column, you can just add a single column header for this column:
ConvertFrom-SourceTable $Text -Header ' Name Company Code Comment' | Format-Table
Name Company Code Comment
---- ------- ---- -------
Bob 02 02 10 80 Enquiries
Martin Corp 02 02 10 80 Langar
As long as you keep the surrounding columns Company and Comment (including the header) also left aligned. If that is not the case (and in some seldom other situations), you might also add a -Ruler parameter (see Help ConvertFrom-SourceTable -Full) to further define the alignment of your table.
Specific to your answer:
As you can see from the example, the ConvertFrom-SourceTable cmdlet returns a list of objects (common to most other cmdlets) which you can easialy pipe to native cmdlets like Where-Object and ForEach-Object. In you specific case you might do something like this:
$Accounts = ConvertFrom-SourceTable $Text -Header `
' Name Company Code Comment'
(($Accounts | Where-Object Name -eq 'Bob').PSObject.Properties).Value
Resulting in:
Bob
02 02 10 80
Enquiries

Insert spaces in the string array

I have the following array:
declare -a case=("060610" "080813" "101016" "121219" "141422")
I want to generate another array where the elements have whitespaces inserted appropriately as:
"06 06 10" "08 08 13" "10 10 16" "12 12 19" "14 14 22"
I got till handling the elements individually using sed as:
echo '060610' | sed 's/../& /g'
But I am not being able to do it while using the array. The sed confuses the white spaces in between the elements and I am left with the output:
echo "${case[#]}" | sed 's/../& /g'
Gives me:
06 06 10 0 80 81 3 10 10 16 1 21 21 9 14 14 22
Can someone help?
You need to loop over the array, not echo it as a whole, because you don't get the grouping when you echo it.
declare -a newcase
for c in "${case[#]}"
do
newcase+=("$(echo "$c" | sed 's/../& /g')")
done
You can use printf '%s\n' "${case[#]}" | sed 's/../& /g' to get each number on a separate line and avoiding the space problem:
$ declare -a case=("060610" "080813" "101016" "121219" "141422")
$ printf '%s\n' "${case[#]}" | sed 's/../& /g'
06 06 10
08 08 13
10 10 16
12 12 19
14 14 22
If you want it back into an array, you can use mapfile

Bash at job not running - Array

I have built a bash script that runs fine when executed from the command line but does not work when run as batch job (with at). First I thought because of the environment but when debugging I think there is a problem with arrays I need to create. When run from command line log is created and its content is what I expected but when run with at any log is created. Any idea for what is causing this issue?
A short script with the piece of code I suppose it is not running is below
#!/bin/bash
fsol=`date +%Y%m%d`
for dia
in 0 1 2
do
var=$(date -d "$fsol +$dia days" +'%Y-%m-%d')
orto=`awk -v j=$var 'BEGIN { FS=","} $2 == j { print $3}' hora-sol.dat`
h_orto=${orto:0:2}
m_orto=${orto:2:2}
a_orto+=($h_orto $m_orto)
echo "dia $dia" $var $h_orto $m_orto >> log1.txt
done
echo ${a_orto[#]} >> log2.txt
Data in hora-sol.dat
32,2016-02-01,0711,1216,1722,10.1885659530428
33,2016-02-02,0710,1216,1723,10.2235441870822
34,2016-02-03,0709,1216,1724,10.2589836910036
35,2016-02-04,0708,1216,1725,10.2948670333624
36,2016-02-05,0707,1216,1727,10.3311771153741
37,2016-02-06,0706,1217,1728,10.3678971831004
38,2016-02-07,0705,1217,1729,10.4050108377139
39,2016-02-08,0704,1217,1730,10.4425020444393
40,2016-02-09,0703,1217,1731,10.4803551390436
41,2016-02-10,0701,1217,1733,10.5185548339287
42,2016-02-11,0700,1217,1734,10.5570862213108
43,2016-02-12,0659,1217,1735,10.5959347763989
44,2016-02-13,0658,1217,1736,10.6350863580571
45,2016-02-14,0657,1217,1737,10.6745272092687
46,2016-02-15,0655,1217,1738,10.7142439549499
47,2016-02-16,0654,1217,1740,10.7542236006922
48,2016-02-17,0653,1217,1741,10.7944535282585
49,2016-02-18,0652,1216,1742,10.8349214920733
50,2016-02-19,0650,1216,1743,10.8756156133281
51,2016-02-20,0649,1216,1744,10.9165243743526
52,2016-02-21,0648,1216,1745,10.9576366115941
53,2016-02-22,0646,1216,1746,10.9989415078031
54,2016-02-23,0645,1216,1747,11.0404285846154
55,2016-02-24,0644,1216,1749,11.0820876932144
56,2016-02-25,0642,1216,1750,11.123909005324
57,2016-02-26,0641,1215,1751,11.1658830035395
58,2016-02-27,0639,1215,1752,11.2080004711946
59,2016-02-28,0638,1215,1753,11.2502524821626
60,2016-02-29,0636,1215,1754,11.2926303895977
Running manually, it generated:
# cat log.txt
dia 0 2016-02-12 0659 1217 1735
dia 1 2016-02-13 0658 1217 1736
dia 2 2016-02-14 0657 1217 1737
06
59
06
58
06
57
Scheduling with at:
# echo "/tmp/horasol/script.sh" | at now +1 minute
warning: commands will be executed using /bin/sh
job 1 at Fri Feb 12 12:11:00 2016
It generated exactly the same:
# cat log.txt
dia 0 2016-02-12 0659 1217 1735
dia 1 2016-02-13 0658 1217 1736
dia 2 2016-02-14 0657 1217 1737
06
59
06
58
06
57
Note that warninig informing that 'at' uses /bin/sh:
warning: commands will be executed using /bin/sh
Tell us how you conclude that "does not work when run as batch job (with at)"
Tell us more about your "when debugging" moment.
Perhaps I'm reproducing here using a different proccess as you. And due to this difference it works for me.

shell programming: define array including zero-padded values

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.

Resources