bash printing and incrementing array values - linux

I'm making a bash script in which I need to print a number while it's incremented like this:
0000
0001
0002
0003
0004
I have made this but is not working:
#!/bin/bash
i=0
pass[0]=0
pass[1]=0
pass[2]=0
pass[3]=0
for i in $(seq 1 9)
pass[3]="$i"
echo ${pass[*]}
done
I paste the script on cli and i get this.
$ ~ #!/bin/bash
$ ~ i=0
$ ~ pass[0]=0
$ ~ pass[1]=0
$ ~ pass[2]=0
$ ~ pass[3]=0
$ ~ for i in $(seq 1 9)
> pass[3]="$i"
bash: error sintáctico cerca del elemento inesperado `pass[3]="$i"'
$ ~ echo ${pass[*]}
0 0 0 0
$ ~ done
bash: error sintáctico cerca del elemento inesperado `done'
$ ~

Use this pure bash script:
for ((i=0; i<10; i++)); do
printf "%04d\n" $i
one
OUTPUT:
0000
0001
0002
0003
0004
0005
0006
0007
0008
0009

#!/bin/bash
i=0
pass[0]=0
pass[1]=0
pass[2]=0
pass[3]=0
for i in $(seq 1 9)
do
pass[3]="$i"
echo ${pass[*]}
done
did you forget 'do'

For those of you who like expansions, you can also do:
printf "%s\n" {0001..0009}
or
printf "%.4d\n" {1..9}
No loop!
You can store in an array thus:
$ myarray=( {0001..0009} )
$ printf "%s\n" "${myarray[#]}"
0001
0002
0003
0004
0005
0006
0007
0008
0009
$ echo "${myarray[3]}"
0004

You can do the formatting with seq:
seq -w 0000 0010
(if you don't like the {0000..0010} notation, which is more efficient but doesn't allow parameter substitution.)

Related

Reading from file bash Linux

I am having a hard time with the following bash script:
basically what the script does is receives a directory and then it searches in all of the folders that are in the directory for files that end with .log. after that it should print to the stdout all the lines from those files sorted by the date they were written in.
my script is this:
#!/bin/bash
find . -name ".*log" | cat *.log | sort --stable --reverse --key=2,3
when i run the script it does return the list but the sort doesnt work properly. my guess is because in some files there are \n which makes it start a new line.
is there a way to ignore the \n that are in the file while still having each line return on a new line?
thank you!
xxd command output:
ise#ise-virtual-machine:~$ xxd /home/ise/Downloads/f1.log
00000000: 3230 3139 2d30 382d 3232 5431 333a 3333 2019-08-22T13:33
00000010: 3a34 342e 3132 3334 3536 3738 3920 4865 :44.123456789 He
00000020: 6c6c 6f0a 576f 726c 640a 0032 3032 302d llo.World..2020-
00000030: 3031 2d30 3154 3131 3a32 323a 3333 2e31 01-01T11:22:33.1
00000040: 3233 3435 3637 3839 206c 6174 650a 23456789 late.
ise#ise-virtual-machine:~$ xxd /home/ise/Downloads/f2.log
00000000: 3230 3139 2d30 392d 3434 5431 333a 3434 2019-09-44T13:44
00000010: 3a32 312e 3938 3736 3534 3332 3120 5369 :21.987654321 Si
00000020: 6d70 6c65 206c 696e 650a mple line.
ise#ise-virtual-machine:~$ xxd /home/ise/Downloads/f3.log
00000000: 3230 3139 2d30 382d 3232 5431 333a 3333 2019-08-22T13:33
00000010: 3a34 342e 3132 3334 3536 3738 3920 4865 :44.123456789 He
00000020: 6c6c 6f0a 576f 726c 6420 320a 0032 3032 llo.World 2..202
00000030: 302d 3031 2d30 3154 3131 3a32 323a 3333 0-01-01T11:22:33
00000040: 2e31 3233 3435 3637 3839 206c 6174 6520 .123456789 late
00000050: 320a 2.
Given that the entries in the log file are terminated with \0 (NUL), find, sed and sort can be combined:
find . -name '*.log' | xargs sed -z 's/\n//g' | sort -z --key=2,3 --reverse
By assuming each record in the file starts with the date and the option --key=2,3 is not necessary, please try:
find . -name "*.log" -exec cat '{}' \; | sort -z | xargs -I{} -0 echo "{}"
The final command xargs .. echo .. will be necessary to print properly the null-terminated lines.
If you still require --key option, please modify the code as you like. I'm not aware how the lines look like as of now.
[UPDATE]
According to the provided information by the OP, I assume the format of the log files
will be:
Each record starts with the date in "yyyy-mm-ddTHH:MM:SS.nanosec" format
and a simple dictionary order sort can be applied.
Each record ends with "\n\0" except for the last record of the file
which ends just with "\n".
Each record may contain newline character(s) in the middle as a part
of the record for the line folding purpose.
Then how about:
find . -name "*.log" -type f -exec cat "{}" \; -exec echo -ne "\0" \; | sort -z
echo -ne "\0" appends a null character to the last record of a file.
Otherwise the record will be merged to the next record of another file.
The -z option to sort treats the null character as a record separator.
No other option to sort will be required so far.
Result with the posted input by the OP:
2019-08-22T13:33:44.123456789 Hello
World
2019-08-22T13:33:44.123456789 Hello
World 2
2019-09-44T13:44:21.987654321 Simple line
2020-01-01T11:22:33.123456789 late
2020-01-01T11:22:33.123456789 late 2
It still keeps the null character "\0" at the end of each record.
If you want to trim it off, please add the tr -d "\0" command
at the end of the pipeline as:
find . -name "*.log" -type f -exec cat "{}" \; -exec echo -ne "\0" \; | sort -z | tr -d "\0"
Hope this helps.

Using tr to trim newlines from command-line argument ignored

I have a shell script that needs to trim newline from input. I am trying to trim new line like so:
param=$1
trimmed_param=$(echo $param | tr -d "\n")
# is the new line in my trimmed_param? yes
echo $trimmed_param| od -xc
# if i just run the tr -d on the data, it's trimmed.
# why is it not trimmed in the dynamic execution of echo in line 2
echo $param| tr -d "\n" |od -xc
I run it from command line as follows:
sh test.sh someword
And I get this output:
0000000 6f73 656d 6f77 6472 000a
s o m e w o r d \n
0000011
0000000 6f73 656d 6f77 6472
s o m e w o r d
0000010
The last command in the script echos what I would think trimmed_param would be if the tr -d "\n" had worked in line 2. What am I missing?
I realize I can use sed etc but ... I would love to understand why this method is failing.
There has never been a newline in the param. It's the echo which appends the newline. Try
# script.sh
param=$1
printf "%s" "${param}" | od -xc
Then
bash script.sh foo
gives you
0000000 6f66 006f
f o o
0000003

What is the difference can be tested between mapfile and mapfile -t?

Sample 1 with option t:
f(){
mapfile -t aaa < /dev/stdin
echo -e ${aaa[#]}
}
echo -e "a\nb\nc" | f
Sample 2 without option t:
f(){
mapfile aaa < /dev/stdin
echo -e ${aaa[#]}
}
echo -e "a\nb\nc" | f
The outputs are same:
a b c
EDIT:
Why are there two whitespaces in #cdarke's and #heemayl's answer?
echo -e "a\nb\nc" | f2 | od -b
0000000 141 012 040 142 012 040 143 012 012
0000011
Then I tried this: echo -e "a\tb\tc" | f2 | od -b
0000000 141 011 142 011 143 012 012
0000007
And there are no whitespaces.
Thx in advance!
To see the difference quote ${aaa[#]}:
echo -e "${aaa[#]}"
Without quoting the expansion is being subjected to word splitting according to IFS (and pathname expansion too). As the elements (strings) contain newlines, these will go through word splitting.
Example:
$ f() { mapfile -t aaa < /dev/stdin; echo -e "${aaa[#]}" ;}
$ echo -e "a\nb\nc" | f
a b c
$ g() { mapfile aaa < /dev/stdin; echo -e "${aaa[#]}" ;}
$ echo -e "a\nb\nc" | g
a
b
c
The problem is with the echo:
f1(){
mapfile -t aaa < /dev/stdin
echo -e "${aaa[#]}" # note the quotes
}
echo -e "a\nb\nc" | f1
f2(){
mapfile aaa < /dev/stdin
echo -e "${aaa[#]}" # note the quotes
}
echo -e "a\nb\nc" | f2
Gives:
a b c
a
b
c
In the second case, without the quotes, the newlines are seen as whitespace.

Bash script check if zip file is empty

I am rather inexperienced in linux,
I have to check in bash script if some zip file is empty - i.e zip contains no files.
I found this code:
if ! zipinfo ${filetotransfer} | tail -n 1 | grep '^0 ' >/dev/null ; then # we have empty zip file!
echo " zip empty"
rm $filetotransfer
exit 0
fi
But it removes file both if zip is empty or not.
Is there any way to check it?
You can just check file size is 22 with stat or md5sum or zip header
# by checking file size
% stat -f%z a.zip
22
% xxd a.zip
0000000: 504b 0506 0000 0000 0000 0000 0000 0000 PK..............
0000010: 0000 0000 0000 ......
# with md5sum
$ md5sum a.zip
76cdb2bad9582d23c1f6f4d868218d6c a.zip
# or by checking zip header
% [ `head -n22 a.zip | tr -d '\0-\6'` = "PK" ] && echo 1
1
You can check the error status of zipinfo -t
f=test.zip
if zipinfo -t "$f" > /dev/null
then
echo "not empy"
else
echo "empty"
fi

How to remove the last CR char with `cut`

I would like to get a portion of a string using cut. Here a dummy example:
$ echo "foobar" | cut -c1-3 | hexdump -C
00000000 66 6f 6f 0a |foo.|
00000004
Notice the \n char added at the end.
In that case there is no point to use cut to remove the last char as follow:
echo "foobar" | cut -c1-3 | rev | cut -c 1- | rev
I will still get this extra and unwanted char and I would like to avoid using an extra command such as:
shasum file | cut -c1-16 | perl -pe chomp
The \n is added by echo. Instead, use printf:
$ echo "foobar" | od -c
0000000 f o o b a r \n
0000007
$ printf "foobar" | od -c
0000000 f o o b a r
0000006
It is funny that cut itself also adds a new line:
$ printf "foobar" | cut -b1-3 | od -c
0000000 f o o \n
0000004
So the solution seems using printf to its output:
$ printf "%s" $(cut -b1-3 <<< "foobar") | od -c
0000000 f o o
0000003

Resources