Require efficient and probably one to two liner solution to replace space - linux

Having following bash scripts to find index of the command from command list
#!/bin/bash
cmdlist="cmd1,cmd2,cmd,cmd24,cmd25,cmd4,cmd10,cmd9,cmd000001,cmdxyz"
cmdlist="${cmdlist//,/ }" #To Replace , with space in array list
cmdlist="${cmdlist//cmd/ }" #To get index of command
echo $cmdlist //Added for clarification
for a in $cmdlist
do
if [[ $a == *[^[:digit:]]* ]] || [[ $a -gt 50 ]]
then
printf "invalid command index : %s\n" $a
else
printf "command index : %s\n" $a
fi
done
As you can see how I extract command index from command list but it's failing (of course it fails) in some condition. So want make some validations as follows:
1) In list, if argument is cmd then it will skipped and not replace space instead cmd string because argument length must be greater then 3.
2) In list, if argument is cmd0001 then also skipped and not replace space instead cmd string because argument length must be less or equal 5 and greater then 3.
Following above validation I achieved by taking for..loop, taking temporary array then compare each argument and validate then store in temporary array and finally copy temporary array in original one.So this is too long procedure.
Any one have idea for batter solution?
Like
cmdlist="${cmdlist//cmd/ }" #To get index of command
command only replace space instead cmd in target argument if condition [[ length -gt 3 ]] && [[ length -le 5 ]] match.
Note: have already have solution using for..loop.
UPDATE: Added more detail for what i want
I got output this from script
command index : 1
command index : 2
command index : 24
command index : 25
command index : 4
command index : 10
command index : 9
command index : 000001
invalid command index : xyz
but i want this
command index : 1
command index : 2
invalid command index : cmd
command index : 24
command index : 25
command index : 4
command index : 10
command index : 9
command index : cmd000001
invalid command index : cmdxyz
So basically leave the argument which not in validation range and mark as invalid index(nothing to do not require to replace space in place of cmd string.)
More UPDATE: Again added more detail to clarify exactly what i want
Have added one echo statement before for..loop in my script ( see modified above script ) which give me output like this
1 2 24 25 4 10 9 000001 xyz
but i want
1 2 cmd 24 25 4 10 9 cmd000001 cmdxyz
means leave argument as it is if it violate validation like in my list third argument is cmd.It violate the condition becasue it's length not greater then 3.Now see last two argument in list cmd000001,cmdxyz It violate the condition because it's length greater then 5. Valid argument is one for which length must be greater then 3 && less or equal 5.
Hope this will clarify what i want.

Editing as per your update:
Get the values in an array and check within the loop if those meet the required criteria:
cmdlist="cmd1,cmd2,cmd,cmd24,cmd25,cmd4,cmd10,cmd9,cmd000001,cmdxyz"
IFS=, read -a arr <<< "$cmdlist"
for a in "${arr[#]}"
do
v="${a/cmd/}"
if ((v > 50)) || ((v <= 0))
then
printf "invalid command index : %s\n" $a
else
printf "command index : %s\n" $v
fi
done
For your input, it'd produce:
command index : 1
command index : 2
invalid command index : cmd
command index : 24
command index : 25
command index : 4
command index : 10
command index : 9
command index : 000001
invalid command index : cmdxyz
Old answer:
Instead of attempting to replace , with spaces and so on, read the string delimited by comma into an array. Manipulate the array to get one containing the desired strings.
$ IFS=, read -a arr <<< "$cmdlist"
$ foo=(${arr[#]/cmd/})
$ for i in "${foo[#]}"; do echo $i; done
1
2
24
25
4
10
9
000001
xyz
$
Checking...
for a in "${foo[#]}"
do
if [[ $a == *[^[:digit:]]* ]] || [[ $a -gt 50 ]]
then
printf "invalid command index : %s\n" $a
else
printf "command index : %s\n" $a
fi
done
produces:
command index : 1
command index : 2
command index : 24
command index : 25
command index : 4
command index : 10
command index : 9
command index : 000001
invalid command index : xyz
A note of caution: Numbers with leading zeros (as you have in your example) would be considered as octal and might produce unexpected results:
$ [[ 0024 -gt 22 ]] && echo greater || echo smaller
smaller

Related

Iterate variables in a file to check for a particular value in bash

Below is my requirement. I have a text file that has following content
File name - abc.txt
Content -
apple=0
mango=1
strawberry=10
I need to kick off the subsequent process only if any of the above stated variable has non zero values.
In this case, As two variables have values 1 and 10 respectively, I need to update an indicator - SKIP INDICATOR=N
If all variables have 0 as value, I need to update SKIP INDICATOR=Y
How to achieve this functionality in Linux. Kindly advise.
with very simple greps :
if [ $(grep '=' your_file | grep -v '=0') ]
then
echo "non zero values detected"
SKIP_INDICATOR=N
else
echo "all are zeroes"
SKIP_INDICATOR=Y
fi
Just note that this is a quick and dirty solution and it would NOT work properly if you have for example a=01 or a= 0 (eg with space)
Try:
grep -q '=0*[1-9]' textfile && skip_indicator=N || skip_indicator=Y
=0*[1-9] matches an '=' character followed by zero or more '0' characters followed by a digit in the range 1 to 9.
See Correct Bash and shell script variable capitalization for an explanation of why I changed SKIP_INDICATOR to skip_indicator.
#!/bin/bash
flag=`awk -F'=' '$NF!="0"{print;exit}' input`
if [ ! -z $flag ] ; then
SKIP_INDICATOR=N
echo "some variable value is different from 0. do something"
else
SKIP_INDICATOR=Y
echo "all variables have 0 as value. do another thing."
fi
exit 0

How to find the location(s) of specific characters in a string

With the aid of this question, I can find out if a string holds a specific character. I want to be able to find out where the character actually is. For example for the string banana, how would I be able to determine the letter n is the 3rd and 5th letter, or for the letter a is the 2nd,4th and 6th letter. and b is the first letter.
Q: For a given string, how can I find the location of a given character in that string?
You can do it with a for loop.
char=a
string=banana
len=${#string}
for (( i=0; i < len; i++ )); do
if [[ $char == ${string:$i:1} ]]
then echo $i
fi
done
The positions printed are zero-based. You could echo $((i+1)) to get 1-based positions instead.
${string:$i:1} extracts the ith character of the string, using bash's substring operator, as explained in Shell Parameter Expansion:
${parameter:offset:length}
This is referred to as Substring Expansion. It expands to up to length characters of the value of parameter starting at the character specified by offset.
Here's a fancy way to do it:
#!/usr/bin/env bash
findChar(){
string="${1}"
char="${2}"
length=${#string}
offset=0
r=()
while true; do
string="${string#*${char}}"
length_new="${#string}"
if [[ "${length}" == "${length_new}" ]]; then
echo "${r[#]}"
return
fi
offset=($(( $offset + $length - $length_new )))
r+=("${offset}")
length="${length_new}"
done
}
findChar banana b
findChar banana a
Here's my take on this:
#!/usr/bin/env bash
[[ ${BASH_VERSINFO[0]} < 4 ]] && { echo "Requires bash 4."; exit 1; }
string="${1:-banana}"
declare -A result=()
for ((i=0; i<${#string}; i++)); do
result[${string:$i:1}]="${result[${string:$i:1}]} $i"
done
declare -p result
The idea is that we walk through the string, adding character positions to strings that are values in an array whose subscripts are the letters you're interested in. It's quick & easy, and gives you a result set you can manipulate afterwards, rather than just sending things to stdout.
My result with this is:
$ ./foo
declare -A result='([a]=" 1 3 5" [b]=" 0" [n]=" 2 4" )'
$ ./foo barber
declare -A result='([a]=" 1" [b]=" 0 3" [e]=" 4" [r]=" 2 5" )'
Results are zero-based (i.e. "b" is in position 0).
Note an interesting side-effect of this method is that every position is preceded by a space, so if you want to count the number of occurrences of a character, you can just count the spaces:
$ declare -A result
$ result[a]=" 1 3 5"
$ count="${result[a]//[0-9]/}"
$ echo "${#count}"
3
$
I don't know what you're planning to do with this data, but if you like, you could easily turn these string results into arrays of their own for easier handling within bash.
Note that associative arrays were introduced with bash version 4.

How to compare variables in bash

I'm begginer in linux console; I want to create if statement with integer variables
if[$x= [$#-2]]
But console receive if can't find this statment if[1 = [5-2]]
Please help me and correct my statement.
You need Arithmetic Expansion: $((expression))
if [ $x = $(($# - 2)) ]; then
# ^ ^ ^ ^ ^ spaces are mandatory
To start $# is the number of parameters passed to the bash script
./bash_script 1 2 3
$# auto-magically populates to 3. I hope you know this already.
#!/bin/bash
x=1
#If you are trying to compare `$x` with the value of the expression `$# - 2` below is how you do it :
if (( $x == $# - 2 ))
then
echo "Some Message"
fi
#If you are trying to check the assignment to `$x was successful below is how you do it :
if (( x = $# - 2 ))
then
echo "Some Message"
fi
The second condition is almost always true, but the first can be false.
Below are the results of my test runs :
#Here the both ifs returned true
sjsam#WorkBox ~/test
$ ./testmath1 1 2 3
Some Message
Some Message
#Here the first if returned false because we passed 4 parameters
sjsam#WorkBox ~/test
$ ./testmath1 1 2 3 4
Some Message

Bash, print 0 in terminal each time a non recognised argument is input

I have a bash program which extracts marks from a file that looks like this:
Jack ex1=5 ex2=3 quiz1=9 quiz2=10 exam=50
I want the code to execute such that when I input into terminal:
./program -ex1 -ex2 -ex3
Jack does not have an ex3 in his data, so an output of 0 will be returned:
Jack 5 3 0
how do I code my program to output 0 for each unrecognized argument?
If I understand what you are trying to do, it isn't that difficult. What you need to do is read each line into a name and the remainder into marks. (input is read from stdin)
Then for each argument given on the command line, check if the first part matches the beginning of any grade in marks (the left size of the = sign). If it does, then save the grade (right side of the = sign) and set the found flag to 1.
After checking all marks against the first argument, if the found flag is 1, output the grade, otherwise output 0. Repeat for all command line arguments. (and then for all students in file) Let me know if you have questions:
#!/bin/bash
declare -i found=0 # initialize variables
declare -i grade=0
while read -r name marks; do # read each line into name & marks
printf "%s" "$name" # print student name
for i in "$#"; do # for each command line argument
found=0 # reset found (flag) 0
for j in $marks; do # for each set of marks check for match
[ $i = -${j%=*} ] && { found=1; grade=${j#*=}; } # if match save grade
done
[ $found -eq 1 ] && printf " %d" $grade || printf " 0" # print grade or 0
done
printf "\n" # print newline
done
exit 0
Output
$ bash marks_check.sh -ex1 -ex2 -ex3 < dat/marks.txt
Jack 5 3 0

Problem with bash code

function dec_to_bin {
if [ $# != 2 ]
then
return -1
else
declare -a ARRAY[30]
declare -i INDEX=0
declare -i TEMP=$2
declare -i TEMP2=0
while [ $TEMP -gt 0 ]
do
TEMP2="$TEMP%2"
#printf "%d" "$TEMP2"
ARRAY[$INDEX]=$TEMP2
TEMP=$TEMP/2
INDEX=$[ $INDEX + 1 ] #note
done
for (( COUNT=INDEX; COUNT>-1; COUNT--)){
printf "%d" "${ARRAY[$COUNT]}" <<LINE 27
#echo -n ${ARRAY[$COUNT]} <<LINE 28
}
fi
}
why is this code giving this error
q5.sh: line 27: ARRAY[$COUNT]: unbound variable
same error comes with line 28 if uncommented
One more question, I am confused with the difference b/w '' and "" used in bash scripting any link to some nice article will be helpfull.
It works fine for me except that you can't do return -1. The usual error value is 1.
The error message is because you have set -u and you're starting your for loop at INDEX instead of INDEX-1 (${ARRAY[INDEX]} will always be empty because of the way your while loop is written). Since you're using %d in your printf statement, empty variables will print as "0" (if set -u is not in effect).
Also, it's meaningless to declare an array with a size. Arrays in Bash are completely dynamic.
I would code the for loop with a test for 0 (because the -1 looks confusing since it can't be the index of an numerically indexed array):
for (( COUNT=INDEX - 1; COUNT>=0; COUNT--))
This form is deprecated:
INDEX=$[ $INDEX + 1 ]
Use this instead:
INDEX=$(( $INDEX + 1 ))
or this:
((INDEX++))
I also recommend using lower case or mixed case variables as a habit to reduce the chance of variable name collision with shell variables.
You're not using $1 for anything.

Resources