I am working on the below script.
I don't understand why I am unable to assign the value to the array and when I print the array elements I only get arr[0], the rest of elements are empty
Is there a more efficient way to match l_num between fields rt_h and rt_f extracted from rt_facts file?
$ bash -version
GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)
$ cat rt_facts
T5 1 2:8 47:9 44
T14 2 48:8 93:9 44
T15 3 94:8 96:9 1
here is the filtered file:
$cat filtered_file
12 4046580009982686 05072021 24692161126100379438583 44442
54 4046580009982686 05072021 24692161126100379438583 44442
95 4046580009982686 05072021 24692161126100379438583 44442
In this script, I am trying to match a string in the filtered file using line number from the original file.
bash-4.1$ vi comb_rt_ct.ksh
#!/bin/bash
rows=1
cols=$(($(cat rt_facts | wc -l) + 1))
declare -a arr #=( $(for i in {1..$cols}; do echo 0; done) )
echo "cols " $cols
for l_num in $(grep '4046580009982686 05072021 24692161126100379438583 44442' filtered_file | cut -d" " -f 1)
do
arr[0]="4046580009982686 05072021 24692161126100379438583 44442"
echo "l_num " $l_num
cat rt_facts | while IFS=" " read -r lineStr
do
line=( $lineStr )
#echo ${line[*]}
rt_h=$(echo ${line[2]} | cut -d":" -f 1)
rt_f=$(echo ${line[3]} | cut -d":" -f 1)
if (( l_num > $rt_h && l_num < $rt_f )); then
echo "rt_h rt_f " $rt_h " " $rt_f
echo "line[*] " ${line[*]}
i=${line[1]}
echo "i " $i
if [[ -z "${arr[$i]}" ]]; then
echo "empty"
arr[$i]=0
fi
(( arr[$i]++ ))
echo "arr[$i] "${arr[$i]}
#echo ${line[0]}
break
fi
done
echo
done
echo ${arr[#]}
echo ${arr[*]}
echo ${arr[2]}
Here is the output when I run the script:
bash-4.1$ sh comb_rt_ct.ksh
cols 4
l_num 12
rt_h rt_f 2 47
line[*] T5 1 2:8 47:9 44
i 1
empty
arr[1] 1
l_num 54
rt_h rt_f 48 93
line[*] T14 2 48:8 93:9 44
i 2
empty
arr[2] 1
l_num 95
rt_h rt_f 94 96
line[*] T15 3 94:8 96:9 1
i 3
empty
arr[3] 1
4046580009982686 05072021 24692161126100379438583 44442
4046580009982686 05072021 24692161126100379438583 44442
bash-4.1$
As #glennjackman has pointed out, the question needs to be updated to provide a minimal, reproducible example; so I'm skipping trying to reverse engineer the intent of the script (or figure out the content of the file named filtered_file) and instead focus on one item of interest:
cat t_facts | while IFS=" " read
...
<assign_values_to_array>
...
done
The | while ... invokes a subshell; new values are assigned to the arr[] array within this subshell.
In ksh, when exiting the subshell, the newly updated arr[] values are (effectively) passed to the calling/parent process.
In bash, when exiting the subshell, the newly updated arr[] values are discarded when returning to the calling/parent process.
Because OP's code is running under bash (#!/bin/bash), the changes to arr[] are lost upon exiting the subshell; this in turn means the calling/parent process 'still' has the same initial value in arr[], namely:
arr[0]="4046580009982686 05072021 24692161126100379438583 44442"
One method to address this issue, which should work in ksh and bash:
while IFS=" " read
...
<assign_value_to_array>
...
done < t_facts
This eliminates the subshell invocation and therefore allows the new array assignments to be made in the main process.
Again, I haven't attempted to understand the overall logic of the script, but fwiw ... I'd suggest OP make the suggested code change to the while loop (to eliminate the subshell invocation) to see if the arr[] array is populated with the desired results.
Related
I have 2 files. I need to add the count of rows of the both and write it to 3rd file.
If the content of 3rd file is >25 , i need to print error.txt and if =<25 , i need to print success.txt
Scenario:
file 1(p.csv) , count: 20
file 2 (m.csv), count : 5
file 3 , count 25
--->should print error.txt
I have tried the below code, but file 3 is not getting generated with expected output.
file_1=$(cat p.csv | wc -l)
echo $file_1
file_2=$(cat m.csv | wc -l)
echo $file_2
file_3 = $(`expr $(file_1) + $(file_2)`)
echo $file_3 > file_3.txt
if [ $file_3 -gt 25 ]; then
touch error.txt
else
touch success.txt
fi
Error message:
20
5
count_test.sh: line 16: file_1: command not found
count_test.sh: line 16: file_2: command not found
expr: syntax error
count_test.sh: line 16: file_3: command not found
count_test.sh: line 20: [: -gt: unary operator expected
Some fixes are required, here is my version:
#!/bin/bash
file_1=$(wc -l p.csv | cut -d' ' -f1)
echo "file_1=$file_1"
file_2=$(wc -l m.csv | cut -d' ' -f1)
echo "file_2=$file_2"
file_3=$(( file_1 + file_2 ))
echo "file_3=$file_3"
if [[ $file_3 -gt 25 ]]
then
echo "ERROR"
touch error.txt
else
echo "Success"
touch success.txt
fi
The arithmetic line was modified to use the $(( )) syntax.
you do not need file_3.txt for the if. If you required it for some other reason, you can put bach the echo "$file_3" > file_3.txt" line.
I added a couple echo statements for debugging purposes.
Some errors you made:
echo $file_1
# Not really wrong, but use quotes
echo "$file_1"
Don't use spaces around = in assignment, don't use backtics and use {} around variables
file_3 = $(`expr $(file_1) + $(file_2)`)
# change this to
file_3=$(expr ${file_1} + ${file_2})
# and consider using (( a = b + c )) for calculations
When you only want the final results, consider
if (( $(cat [pm].csv | wc -l) > 25 )); then
touch error.txt
else
touch success.txt
fi
I have a file like this below:
-bash-4.2$ cat a1.txt
0 10.95.187.87 5444 up 0.333333 primary 0 false 0
1 10.95.187.88 5444 up 0.333333 standby 1 true 0
2 10.95.187.89 5444 up 0.333333 standby 0 false 0
I want to fetch the data from the above file into a 2D array.
Can you please help me with a suitable way to put into an array.
Also post putting we need put a condition to check whether the value in the 4th column is UP or DOWN. If it's UP then OK, if its down then below command needs to be executed.
-bash-4.2$ pcp_attach_node -w -U pcpuser -h localhost -p 9898 0
(The value at the end is getting fetched from the 1st column.
You could try something like that:
while read -r line; do
declare -a array=( $line ) # use IFS
echo "${array[0]}"
echo "${array[1]}" # and so on
if [[ "$array[3]" ]]; then
echo execute command...
fi
done < a1.txt
Or:
while read -r -a array; do
if [[ "$array[3]" ]]; then
echo execute command...
fi
done < a1.txt
This works only if field are space separated (any kind of space).
You could probably mix that with regexp if you need more precise control of the format.
Firstly, I don't think you can have 2D arrays in bash. But you can however store lines into a 1-D array.
Here is a script ,parse1a.sh, to demonstrate emulation of 2D arrays for the type of data you included:
#!/bin/bash
function get_element () {
line=${ARRAY[$1]}
echo $line | awk "{print \$$(($2+1))}" #+1 since awk is one-based
}
function set_element () {
line=${ARRAY[$1]}
declare -a SUBARRAY=($line)
SUBARRAY[$(($2))]=$3
ARRAY[$1]="${SUBARRAY[#]}"
}
ARRAY=()
while IFS='' read -r line || [[ -n "$line" ]]; do
#echo $line
ARRAY+=("$line")
done < "$1"
echo "Full array contents printout:"
printf "%s\n" "${ARRAY[#]}" # Full array contents printout.
for line in "${ARRAY[#]}"; do
#echo $line
if [ "$(echo $line | awk '{print $4}')" == "down" ]; then
echo "Replace this with what to do for down"
else
echo "...and any action for up - if required"
fi
done
echo "Element access of [2,3]:"
echo "get_element 2 3 : "
get_element 2 3
echo "set_element 2 3 left: "
set_element 2 3 left
echo "get_element 2 3 : "
get_element 2 3
echo "Full array contents printout:"
printf "%s\n" "${ARRAY[#]}" # Full array contents printout.
It can be executed by:
./parsea1 a1.txt
Hope this is close to what you are looking for. Note that this code will loose all indenting spaces during manipulation, but a formatted update of the lines could solve that.
I have a list of numbers, around 200 and at the beginning of my ksh I want to verify if parameter 1 is one of this numbers.
I solved this with a big if, but I think that a more elegant solution must exist.
In example, something like this, but in ksh
if $1 in (50, 28, 500, 700, 1, 47) then
do what I want
else
exit
end if
Any idea to start working?
Thanks.
Luis
I found the solution
case $1 in ( 50 | 28 | 500 | 700 | 1 | 47 )
echo ¨Found!¨
;;
*)
echo ¨NOT found!¨
;;
esac
Thanks!
The case statement works for short lists if the list changes or is long, that can get ugly in a hurry. Another idea is to use an associative array. I set up a list of 100 random numbers in the file rand.txt and ran this script to check for numbers on the list:
typeset -A numList
for num in $( < rand.txt )
do
numList[$num]=$num
done
if [[ -n ${numList[$1]} ]]
then
echo "do what I want"
else
echo 'not interesting'
fi
If you don't want a separate file with the numbers, this also works:
typeset -A numList
(
cat <<EOF
72
107
104
82
20
21
EOF
) | while read num
do
numList[$num]=$num
done
if [[ -n ${numList[$1]} ]]
then
echo "do what I want"
else
echo 'not interesting'
fi
These also work on bash.
First let me say I followed questions on stackoverflow.com that relate to my question and it seems the rules are not applying. Let me show you.
The following script:
#!/bin/bash
OUTPUT_DIR=/share/es-ops/Build_Farm_Reports/WorkSpace_Reports
TODAY=`date +"%m-%d-%y"`
HOSTNAME=`hostname`
WORKSPACES=( "bob" "mel" "sideshow-ws2" )
if ! [ -f $OUTPUT_DIR/$HOSTNAME.csv ] && [ $HOSTNAME == "sideshow" ]; then
echo "$TODAY","$HOSTNAME" > $OUTPUT_DIR/$HOSTNAME.csv
echo "${WORKSPACES[0]}," >> $OUTPUT_DIR/$HOSTNAME.csv
sed -i "/^'"${WORKSPACES[0]}"'/$/'"${WORKSPACES[1]}"'/" $OUTPUT_DIR/$HOSTNAME.csv
sed -i "/^'"${WORKSPACES[1]}"'/$/${WORKSPACES[2]}"'/" $OUTPUT_DIR/$HOSTNAME.csv
fi
I want the output to look like:
09-20-14,sideshow
bob,mel,sideshow-ws2
the sed statements are supposed to append successive array elements to preceding ones on the same line. Now I know there's a simpler way to do this like:
echo "${WORKSPACES[0]},${WORKSPACES[1]},${WORKSPACES[2]}" >> $OUTPUT_DIR/$HOSTNAME.csv
But let's say I had 30 elements in the array and I wanted to appended them one after the other on the same line? Can you show me how to loop through the elements in an array and append them one after the other on the same line?
Also let's say I had the output of a command like:
df -m /export/ws/$ws | awk '{if (NR!=1) {print $3}}'
and I wanted to append that to the end of the same line.
But when I run it I get:
+ OUTPUT_DIR=/share/es-ops/Build_Farm_Reports/WorkSpace_Reports
++ date +%m-%d-%y
+ TODAY=09-20-14
++ hostname
+ HOSTNAME=sideshow
+ WORKSPACES=("bob" "mel" "sideshow-ws2")
+ '[' -f /share/es-ops/Build_Farm_Reports/WorkSpace_Reports/sideshow.csv ']'
And the file right now looks like:
09-20-14,sideshow
bob,
I am happy to report that user syme solved this (see below) but then I realized I need the date in the first column:
09-7-14,bob,mel,sideshow-ws2
Can I do this using syme's for loop?
Okay user syme solved this too he said "Just add $TODAY to the for loop" like this:
for v in "$TODAY" "${WORKSPACES[#]}"
Okay now the output looks like this I changed the elements in the array btw:
sideshow
09-20-14,bob_avail,bob_used,mel_avail,mel_used,sideshow-ws2_avail,sideshow-ws2_used
Now below that the next line will be populated by a , in the first column skipping the date and then:
df -m /export/ws/$v | awk '{if (NR!=1) {print $3}}
which equals the value of available space on bob in the first iteration
and then:
df -m /export/ws/$v | awk '{if (NR!=1) {print $2}}
which equals the value of used space on bob in the 2nd iteration
and then we just move on to the next value in ${WORKSPACE[#]}
which will be mel and do the available and used as we did with bob or $v above.
I know you geniuses on here will make child's play out of this.
I solved my own last question on this thread:
WORKSPACES2=( "bob" "mel" "sideshow-ws2" )
separator="," # defined empty for the first value
for v in "${WORKSPACES2[#]}"
do
available=`df -m /export/ws/$v | awk '{if (NR!=1) {print $3}}'`
used=`df -m /export/ws/$v | awk '{if (NR!=1) {print $2}}'`
echo -n "$separator$available$separator$used" >> $OUTPUT_DIR/$HOSTNAME.csv # append, concatenated, the separator and the value to the file
done
produces:
sideshow
09-20-14,bob_avail,bob_used,mel_avail,mel_used,sideshow-ws2_avail,sideshow-ws2_used
,470400,1032124,661826,1032124,43443,1032108
echo -n permits to print text without the linebreak.
To loop over the values of the array, you can use a for-loop:
echo "$TODAY,$HOSTNAME" > $OUTPUT_DIR/$HOSTNAME.csv # with a linebreak
separator="" # defined empty for the first value
for v in "${WORKSPACES[#]}"
do
echo -n "$separator$v" >> $OUTPUT_DIR/$HOSTNAME.csv # append, concatenated, the separator and the value to the file
separator="," # comma for the next values
done
echo >> $OUTPUT_DIR/$HOSTNAME.csv # add a linebreak (if you want it)
Below KSH script results in the error "Syntax error at line 4: '$' unexpected"
!#/bin/ksh
for i in `cat pins.list`
do
set -A array_${i} `grep -i "$i " pins.txt | awk '{print $2}'`
echo "Elements of array_${i} are ${array_${i}[#]}"
done
#=================================
I am creating multiple arrays (array_$i) for each iteration of i, after parsing the file pins.txt.
I can see the arrays array_block , array_group, array_range created and the elements of pins.txt stored in these arrays correctly, but I am unable to print the values of each of these arrays due to this error. Printing the contents of these 3 arrays outside the loop has no issues. But I need to access these arrays inside the loop for further processing in my script. Is there a way to resolve this?
Contents of pins.list and pins.txt are as follows:
pins.list (Arrays)
==================
block
group
range
pins.txt
===========
range 444
group 46
range 32
block 96
group 99
range 123
block 56
range 22
Thanks
You cannot create a dynamic variable name in this way, you need eval. For example:
while read i
do
eval "set -A array_${i} \$(grep -i $i pins.txt | awk '{print $2}')"
eval "echo \"Elements of array_${i} are \${array_${i}[#]}\" "
done < pins.list
I have changed from a for loop to a while, this is an alternative method of reading a file rather than using cat (also, check your #! line).