How to match substring with special characters in linux shell? - linux

I wanted to check if a file contains a string in bash. I have done so for regular string characters. However, i need to check a new string which contains double quote ".
Say the string is:
PARAMETER_XYZ="no"
I tried this, but does not work:
ABC=$(cat /etc/file)
if [[ $ABC = *"PARAMETER_XYZ=\"no\""*]] ; then
exit 0
fi
Any suggestions?

grep is the program of choice to look for the presence of strings.
if grep 'PARAMETER_XYZ="no"' /etc/file > /dev/null then
exit 0
fi
You can also still do it with [[ if you really want:
ABC=$(cat /etc/file)
if [[ $ABC = *'PARAMETER_XYZ="no"'* ]] ; then
exit 0
fi
However, if that's actually a config file you're trying to parse, there are better solutions that are less fragile than looking for an exact string. That file looks like it might even be a shell variables file, in which case you could just source it and then check $PARAMETER_XYZ directly.

Related

Shell Script working with multiple files [duplicate]

This question already has answers here:
How to iterate over arguments in a Bash script
(9 answers)
Closed 5 years ago.
I have this code below:
#!/bin/bash
filename=$1
file_extension=$( echo $1 | cut -d. -f2 )
directory=${filename%.*}
if [[ -z $filename ]]; then
echo "You forgot to include the file name, like this:"
echo "./convert-pdf.sh my_document.pdf"
else
if [[ $file_extension = 'pdf' ]]; then
[[ ! -d $directory ]] && mkdir $directory
convert $filename -density 300 $directory/page_%04d.jpg
else
echo "ERROR! You must use ONLY PDF files!"
fi
fi
And it is working perfectly well!
I would like to create a script which I can do something like this: ./script.sh *.pdf
How can I do it? Using asterisk.
Thank you for your time!
Firstly realize that the shell will expand *.pdf to a list of arguments. This means that your shell script will never ever see the *. Instead it will get a list of arguments.
You can use a construction like the following:
#!/bin/bash
function convert() {
local filename=$1
# do your thing here
}
if (( $# < 1 )); then
# give your error message about missing arguments
fi
while (( $# > 0 )); do
convert "$1"
shift
done
What this does is first wrap your functionality in a function called convert. Then for the main code it first checks the number of arguments passed to the script, if this is less than 1 (i.e. none) you give the error that a filename should be passed. Then you go into a while loop which is executed as long as there are arguments remaining. The first argument you pass to the convert function which does what your script already does. Then the shift operation is performed, what this does is it throws away the first argument and then shifts all the remaining arguments "left" by one place, that is what was $2 now is $1, what was $3 now is $2, etc. By doing this in the while loop until the argument list is empty you go through all the arguments.
By the way, your initial assignments have a few issues:
you can't assume that the filename has an extension, your code could match a dot in some directory path instead.
your directory assignment seems to be splitting on . instead of /
your directory assignment will contain the filename if no absolute or relative path was given, i.e. only a bare filename
...
I think you should spend a bit more time on robustness
Wrap your code in a loop. That is, instead of:
filename=$1
: code goes here
use:
for filename in "$#"; do
: put your code here
done

Understanding and Clarification on AIX and Batch File

I am new to AIX and I have trouble understanding the codes stated in the shell script as shown below, I have a few questions.
if [ "$OutChlName" != "" ] ; then
echo START CHANNEL \($OutChlName\)
fi
For the first line, what does the "" mean, does it mean null?
\($OutChlName\) - is there any way to convert this to a batch file format.
Is it right to say that fi is the end tag of if?
Thank you.
The echo is only wanted when the variable OutChkName is filled.
The string is compared with an empty string.
When you read man test, you can find the alternative if [ -n "$OutChlName" ].
echo with double quotes
Within quotes you do not need the backslashes.
echo "START CHANNEL ($OutChlName)"
My echo behaves different when the OutChlName variable has special characters like newlines or *. I think my syntax is a slight bugfix, but when you do not want to change the original behaviour, you can use
echo "START CHANNEL ("$OutChlName")"
Using backslashes is also a valid syntax (batch format).
fi ends if
Also esac ends case, and done ends do (Odd, it should have been od).

if statement in while loop bash

I am trying to make script that inserts hostname and ip address for certain website in mysql , but before the script inserts results i want to check if they already exist and if it does the script should not insert it.
result_check=$(mysql -uroot -p123qwe webs -e "SELECT COUNT(*) AS NUMBER FROM webs WHERE hostname='$hostname' AND ip='$ip'";)
cat $dir/webs | while read hostname ip; do
if [[ $result_check -eq 0 ]]; then
echo "INSERT INTO webs (hostname,ip) VALUES ('$hostname','$ip');"
fi;
done | mysql -uroot -p123qwe webs;
And this somehow does not work , i can think of other way to get the job done(without if) but i want to make it this way.
My webs file looks like that :
somedomain.com 192.168.3.3 ... and etc.
This is the error i get:
line 23: [[: NUMBER
0: syntax error in expression (error token is "0")
I have tried many ways to escape this error (changing the brackets from [[ to (( ) but i've had no luck.
The error message suggests that the output from the first mysql begins with NUMBER. Maybe there's a way to suppress column headers? Ah yes, the -N option.
Secondly, you appear to require the values that are being read from the file to be present in the query, so you have to rearrange things accordingly.
Additionally, you want to clean up the syntax.
while read hostname ip; do
result_check=$(mysql -N -uroot -p123qwe webs \
-e "SELECT COUNT(*) AS NUMBER FROM webs WHERE hostname='$hostname' AND ip='$ip'")
if [[ $result_check -eq 0 ]]; then
echo "INSERT INTO webs (hostname,ip) VALUES ('$hostname','$ip');"
fi
done <"$dir"/webs | mysql -uroot -p123qwe webs
I dislike the temporary variable for the result_check but inlining it would hamper readability, so I guess it'll have to stay.
However, a much better approach would be to use native SQL constructs for INSERT ... WHERE NOT EXISTS or similar. See also How to 'insert if not exists' in MySQL?
Your code is kosher, so $result_check is clearly not a number. Here are some suggestions:
Add an echo if [[ $result_check -eq 0 ]] before the if check to see what's being checked.
In Bash it's sometimes helpful to protect against strings being empty. In this case, the protection would be: if [[ "x$result_check" = x0 ]], but you can't do any interesting integer stuff with that check, for example testing equality for "0" and "00" would fail. if [[ "0$result_check" -eq 0 ]] should solve that problem, but I haven't tested it. On my system, the [[ function actually handles the empty string with no problem, so [[ $undefined_variable -eq 0 ]] is always true.
You can run type [[ to see what the [[ function actually is. For me, it's a shell keyword (bash version 3.2).
For fun, try the [ function instead. As long as you use quotes and add a prefix to protect against empty variables, the result should be the same for these simple tests.

Scripts in sed - linux

I got the concept of bash, now, I found a site full of riddles for practising bash. I solve a couple of scripts (you should mention what do they do, what they are missing or so, depends on the question) and I bumped this script:
random_var="$(echo $1 | sed -e 's/[^[:alnum:]]//g')"
Correct me if I'm wrong about my basic assumptions on the following code:
$1 is the second argument that the script got (when the first is the script name)
There is a pipeline between the second argument and the sed script that removes all alpha numerics and... according to what I understand, this script can be "broken" by using a delimiter such as [/\]^$ and so ?
Now, there comes the difficulty (well, for me), the program gets an input from the user and, when the following script I just mention is found at a function returning true if the input is different than the result. I have no idea what is happening here, can someone enlighten me?
#!/bin/sh
func()
{
somevar="$(echo $1 | sed -e 's/[^[:alnum:]]//g')"
if [ "$somevar" != "$input" ] ; then
return 1
else
return 0
fi
}
# Sample usage of this function in a script
echo -n "Enter input: "
read input
if ! func "$input" ; then
echo "HELL NO"
exit 1
else
echo "YES!"
fi
exit 0
The script tests a string to see whether it contains any non-alphanumeric characters.
As Avinash has mentioned in the comments, the sed command removes all non-alphanumeric characters. Within the function, $input has the same value as it does in the calling scope, which is also the same as the first argument, $1. This is perhaps a little bit confusing...
If $somevar is different to $input (=$1), then this means that sed has changed the string in some way. Therefore, the string must contain at least one non-alphanumeric character.
If the function returns 1 (there were some non-alphanumeric characters in the input), then ! func is false, so the else branch will be executed and the script will return with an exit code of 0 (success). Otherwise, the script will return a non-zero exit code, indicating a failure.

Make sure int variable is 2 digits long, else add 0 in front to make it 2 digits long

How do I check a int variable ($inputNo) to see if it’s 2 or more decimal digits long?
Example:
inputNo="5"
Should be changed to: 05
inputNo="102"
Should be left alone: 102
I thought about using wc and if statements, but wc -m doesn’t seems to give the actual characters passed into wc, as wc always seems to give +1 to the characters that is given.
But I don’t know how to add a 0 in front of the current input number.
You can use the bash-builtin printf with the -v option to write it to a variable rather than print it to standard output:
pax> inputNo=5 ; printf -v inputNo "%02d" $inputNo ; echo $inputNo
05
pax> inputNo=102 ; printf -v inputNo "%02d" $inputNo ; echo $inputNo
102
You'll want to make sure it's numeric first otherwise the conversion will fail. If you want to be able to pad any string out to two or more characters, you can also use:
while [[ ${#inputNo} -lt 2 ]] ; do
inputNo="0${inputNo}"
done
which is basically a while loop that prefixes your string with "0" until the length is greater than or equal to two.
Note that this can also be done in bash by prefixing the number with two zeroes then simply getting the last two characters of that string, checking first that it's not already at least the desired size:
if [[ ${#inputNo} -lt 2 ]] ; then
inputNo="00${inputNo}"
inputNo="${inputNo: -2}"
fi
The difference is probably not too great for a two-digit number but you may find the latter solution is better if you need larger widths.
If you're using a shell other than bash (unlikely, based on your tags), you'll need to find the equivalents, or revert to using external processes to do the work, something like:
while [[ $(echo -n ${inputNo} | wc -c) -lt 2 ]] ; do
inputNo="0${inputNo}"
done
This does basically what you were thinking off in your question but note the use of -n in the echo command to prevent the trailing newline (which was almost certainly causing your off-by-one error).
But, as stated, this is a fall-back position. If you're using bash, the earlier suggestions of mine are probably best.
For general-purpose padding whether the string is numeric or not
No need for piping echo into wc or using a while loop.
In Bash, you can get the length of a string like this: ${#inputNo}.
And since you can do substrings, you can do this instead:
if [[ ${#input} < 2 ]]
then
inputNo="00${inputNo}"
inputNo="${inputNo: -2}"
fi
You can use http://bash-hackers.org/wiki/doku.php/commands/builtin/printf, an example from there:
the_mac="0:13:ce:7:7a:ad"
# lowercase hex digits
the_mac="$(printf "%02x:%02x:%02x:%02x:%02x:%02x" 0x${the_mac//:/ 0x})"
# or the uppercase-digits variant
the_mac="$(printf "%02X:%02X:%02X:%02X:%02X:%02X" 0x${the_mac//:/ 0x})"

Resources