Bash at job not running - Array - arrays

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.

Related

Bad substitution error when passing decremented variable to "sed" command

I like to use "sed" command to delete two consecutive lines from a file.
I can delete single line using following syntax where
variable "index" holds the line number:
sed -i "${index}d" "$PWD$DEBUG_DIR$DEBUG_MENU"
Since I like to delete consecutive lines I did test this
syntax which supposedly decrements / increments the variable but I am getting a bad substitution error.
Addendum
I am not sure where to post this , but during troubleshooting of
this issue I have discovered that this syntax does not work as expected
(( index++)) nor (( index-- ))
Using sed with "index" in single line deletion twice in a row / sequentially / works and resolves few issues.
#delete command line
sed -i "${index}d" "$PWD$DEBUG_DIR$DEBUG_MENU"
#delete description line
sed -i "${index}d" "$PWD$DEBUG_DIR$DEBUG_MENU"
sed is the right tool for doing s/old/new, that is all. What you're trying to do isn't that so why are you trying to coerce sed into doing something when there's far more appropriate tools can do the job far easier?
The right way to do what your script:
#retrieve first matching line number
index=$(sed -n "/$choice/=" "$PWD$DEBUG_DIR$DEBUG_MENU")
#delete matching line plus next line from file
sed -i "/$index[1], (( $index[1]++))/"
"$PWD$DEBUG_DIR$DEBUG_MENU"
seems to be trying to do is this:
awk -v choice="$choice" '$0~choice{skip=2} !(skip&&skip--)' "$PWD$DEBUG_DIR$DEBUG_MENU"
For example:
$ seq 20 | awk -v choice="4" '$0~choice{skip=2} !(skip&&skip--)'
1
2
3
6
7
8
9
10
11
12
13
16
17
18
19
20
if you only want to delete the first match:
$ seq 20 | awk -v choice="4" '$0~choice{skip=2;cnt++} !(cnt==1 && skip && skip--)'
1
2
3
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
or the 2nd:
$ seq 20 | awk -v choice="4" '$0~choice{skip=2;cnt++} !(cnt==2 && skip && skip--)'
1
2
3
4
5
6
7
8
9
10
11
12
13
16
17
18
19
20
and to skip 5 lines instead of 2:
$ seq 20 | awk -v choice="4" '$0~choice{skip=5} !(skip&&skip--)'
1
2
3
9
10
11
12
13
19
20
Just use the right tool for the right job, don't go digging holes to plant trees with a teaspoon.
If you just want the first one, then quit when you see it:
sed -n "/$choice/ {=;q}" file
But you look like you're processing this file multiple times. There must be a simpler way to do it, if you can describe your over-arching goal.
For example, if you just want to remove the matched line and the next line, but only the first time, you can use awk: here we see "4" and "5" are gone, but "14" and "15" remain:
$ seq 20 | awk '/4/ && !seen {getline; seen++; next} 1'
1
2
3
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
With GNU sed, you can use +1 as the second address in an address range:
index=4
seq 10 | sed "$index,+1 d"
Or if you want to use bash/ksh arithmetic expansion: use the post-increment operator
seq 10 | sed "$((index++)),$index d"
Note here that, due to the pipeline, sed is running in a subshell: even though the index value is now 5, this occurs in a subshell. After the sed command ends, index has value 4 in the current shell.

trying to run arbitrary commands and parse their output

here is part of code
scanf("%[^\n]%*c",command);
int pid;
pid=fork();
if (pid == 0) {
// Child process
char *argv[]={command ,NULL};
execvp(argv[0], argv);
exit (0);
}
When I give as input ls I want as output
1 copy of mysh1.c mysh1.c mysh3.c mysh.c New Folder
a.out helpmanual.desktop mysh2.c mysh4.c New File
and when i give ls -l /tmp
i'm waiting
total 12
-rw------- 1 antre antre 0 Nov 4 17:31 config-err-KT9sEZ
drwx------ 2 antre antre 4096 Nov 4 19:21 mozilla_antre0
drwx------ 2 antre antre 4096 Jan 1 1970 orbit-antre
drwx------ 2 antre antre 4096 Nov 4 17:31 ssh-HaOFtKdeIQnQ `
but i take:
1 copy of mysh1.c mysh1.c mysh3.c mysh.c New Folder
a.out helpmanual.desktop mysh2.c mysh4.c New File
It seems that you're trying to parse the output of ls -l in a C program for some reason.
That's unlikely to be the “right” thing to do. The usual mechanism is to use opendir and readdir to read the directory file, directly.
If you have some truly strange situation in which you cannot opendir (the only case that comes to mind is if you're running ls on a remote system, eg, over ssh), there is a mode in GNU ls specifically for producing an output record format that can be parsed by another program.
From the GNU coreutils info:
10.1.2 What information is listed
‘-D’
‘--dired’
With the long listing (‘-l’) format, print an additional line after
the main output:
//DIRED// BEG1 END1 BEG2 END2 ...
The BEGN and ENDN are unsigned integers that record the byte
position of the beginning and end of each file name in the output.
This makes it easy for Emacs to find the names, even when they
contain unusual characters such as space or newline, without fancy
searching.
If directories are being listed recursively (‘-R’), output a
similar line with offsets for each subdirectory name:
//SUBDIRED// BEG1 END1 ...
Finally, output a line of the form:
//DIRED-OPTIONS// --quoting-style=WORD
where WORD is the quoting style (*note Formatting the file
names::).
Here is an actual example:
$ mkdir -p a/sub/deeper a/sub2
$ touch a/f1 a/f2
$ touch a/sub/deeper/file
$ ls -gloRF --dired a
a:
total 8
-rw-r--r-- 1 0 Jun 10 12:27 f1
-rw-r--r-- 1 0 Jun 10 12:27 f2
drwxr-xr-x 3 4096 Jun 10 12:27 sub/
drwxr-xr-x 2 4096 Jun 10 12:27 sub2/
a/sub:
total 4
drwxr-xr-x 2 4096 Jun 10 12:27 deeper/
a/sub/deeper:
total 0
-rw-r--r-- 1 0 Jun 10 12:27 file
a/sub2:
total 0
//DIRED// 48 50 84 86 120 123 158 162 217 223 282 286
//SUBDIRED// 2 3 167 172 228 240 290 296
//DIRED-OPTIONS// --quoting-style=literal
Note that the pairs of offsets on the ‘//DIRED//’ line above
delimit these names: ‘f1’, ‘f2’, ‘sub’, ‘sub2’, ‘deeper’, ‘file’.
The offsets on the ‘//SUBDIRED//’ line delimit the following
directory names: ‘a’, ‘a/sub’, ‘a/sub/deeper’, ‘a/sub2’.
Here is an example of how to extract the fifth entry name,
‘deeper’, corresponding to the pair of offsets, 222 and 228:
$ ls -gloRF --dired a > out
$ dd bs=1 skip=222 count=6 < out 2>/dev/null; echo
deeper
Note that although the listing above includes a trailing slash for
the ‘deeper’ entry, the offsets select the name without the
trailing slash. However, if you invoke ‘ls’ with ‘--dired’ along
with an option like ‘--escape’ (aka ‘-b’) and operate on a file
whose name contains special characters, notice that the backslash
is included:
$ touch 'a b'
$ ls -blog --dired 'a b'
-rw-r--r-- 1 0 Jun 10 12:28 a\ b
//DIRED// 30 34
//DIRED-OPTIONS// --quoting-style=escape
If you use a quoting style that adds quote marks (e.g.,
‘--quoting-style=c’), then the offsets include the quote marks. So
beware that the user may select the quoting style via the
environment variable ‘QUOTING_STYLE’. Hence, applications using
‘--dired’ should either specify an explicit
‘--quoting-style=literal’ option (aka ‘-N’ or ‘--literal’) on the
command line, or else be prepared to parse the escaped names.
i just only needed to use strtok

Two arrays with different length in a loop

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

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.

How can I make a batch file to run the same cloc command with one click?

To run the command:
cloc-1.08.exe wizardry
Obviously I don't want to go into DOS every time I want to do a line count.
It's as simple as the pause command:
x.bat:
#echo off
cloc "C:\temp\lua"
pause
Output in new window after double clicking on x.bat:
99 text files.
96 unique files.
9 files ignored.
http://cloc.sourceforge.net v 1.08 T=2.0 s (45.0 files/s, 13856.5 lines/s)
-------------------------------------------------------------------------------
Language files blank comment code scale 3rd gen. equiv
-------------------------------------------------------------------------------
C 35 2028 762 11170 x 0.77 = 8600.90
HTML 5 2927 12 6718 x 1.90 = 12764.20
C/C++ Header 24 828 796 1511 x 1.00 = 1511.00
Lua 20 69 71 385 x 4.00 = 1540.00
make 3 67 43 244 x 2.50 = 610.00
CSS 2 10 0 44 x 1.00 = 44.00
DOS Batch 1 2 0 26 x 0.63 = 16.38
-------------------------------------------------------------------------------
SUM: 90 5931 1684 20098 x 1.25 = 25086.48
-------------------------------------------------------------------------------
Press any key to continue . . .
pause forces the window to stay open until you hit a key or manually close the command window.
#echo off
cloc-1.08.exe wizardry
pause

Resources