I have next string comparison in bash:
if [[ $1 == "/"* ]]; then echo YES; else echo "$1"; fi
How to do same in sh?
You could do it with a case statement.
case $1 in
/*) echo YES;;
*) echo $1
esac
The direct translation would be if [ "$1" = "/*" ], but it wouldn't work because sh doesn't support glob matches there. You'd need to invoke an external command like grep.
if printf '%s\n' "$1" | grep -e '^/' >/dev/null 2>&1; then
...
fi
This would be shorter with grep -q, but if you don't have bash then you may not have grep -q either.
The case statement handles this elegantly.
case $1 in
'/'* ) echo YES;;
*) echo "$1";;
esac
The lack of quoting before in and the general syntax with the double semicolons and unpaired right parentheses is jarring to the newcomer, but you quickly get used to it. It's quite versatile and much under-appreciated.
If you insist on using [ you could perhaps do something like
if temp=${1#?}; [ "${1%$temp}" -eq '*' ]; then
...
which uses a couple of parameter expansions to extract the first character of the variable; but case has glob pattern matching built in, so it's considerably more readable.
If you would like to match a pattern, or regex in if statements if sh or fish you can use
if echo "$1" | grep -q ^/; then echo yes; else echo "$1"; fi
Related
I want to ensure that the given arguments are flags, by which I mean -x where x is some character or sequence of.
I have tried to do this with the following:
if [[ "$(echo '$1' | sed 's/[^-]//g')" -ne "-" ]];
then
echo "$usage"
exit
fi
Where my reasoning is that if - is not present when other characters are stripped it's not a flag.
This doesn't work though, and is obviously flimsy, but I don't know how to do this correctly.
# valid
script.sh -asdf
# invalid
script.sh sdf
You can do it this way to make sure $1 is starting with -:
if [[ "${1?}" != -* ]]; then
echo "$usage"
exit 1
fi
${1?} will fail the script if $1 is not available.
I am taking baby steps at learning bash and I am developing a piece of code which takes an input and checks if it contains any spaces. The idea is that the variable should NOT contain any spaces and the code should consequently echo a suitable message.
Try this:
#!/bin/bash
if [[ $1 = *[[:space:]]* ]]
then
echo "space exist"
fi
You can use grep, like this:
echo " foo" | grep '\s' -c
# 1
echo "foo" | grep '\s' -c
# 0
Or you may use something like this:
s=' foo'
if [[ $s =~ " " ]]; then
echo 'contains space'
else
echo 'ok'
fi
You can test simple glob patterns in portable shell by using case, without needing any external programs or Bash extensions (that's a good thing, because then your scripts are useful to more people).
#!/bin/sh
case "$1" in
*' '*)
printf 'Invalid argument %s (contains space)\n' "$1" >&2
exit 1
;;
esac
You might want to include other whitespace characters in your check - in which case, use *[[:space:]]* as the pattern instead of *' '*.
You can use wc -w command to check if there are any words. If the result of this output is a number greater than 1, then it means that there are more than 1 words in the input. Here's an example:
#!/bin/bash
read var1
var2=`echo $var1 | wc -w`
if [ $var2 -gt 1 ]
then
echo "Spaces"
else
echo "No spaces"
fi
Note: there is a | (pipe symbol) which means that the result of echo $var1 will be given as input to wc -w via the pipe.
Here is the link where I tested the above code: https://ideone.com/aKJdyN
You could use parameter expansion to remove everything that isn't a space and see if what's left is the empty string or not:
var1='has space'
var2='nospace'
for var in "$var1" "$var2"; do
if [[ ${var//[^[:space:]]} ]]; then
echo "'$var' contains a space"
fi
done
The key is [[ ${var//[^[:space:]]} ]]:
With ${var//[^[:space:]]}, everything that isn't a space is removed from the expansion of $var.
[[ string ]] has a non-zero exit status if string is empty. It's a shorthand for the equivalent [[ -n string ]].
We could also quote the expansion of ${var//[^[:space:]]}, but [[ ... ]] takes care of the quoting for us.
I am new to bash scripting and I have to create this script that takes 3 directories as arguments and copies in the third one all the files in the first one that are NOT in the second one.
I did it like this:
#!/bin/bash
if [ -d $1 && -d $2 && -d $3 ]; then
for FILE in [ ls $1 ]; do
if ! [ find $2 -name $FILE ]; then
cp $FILE $3
done
else echo "Error: one or more directories are not present"
fi
The error I get when I try to execute it is: "line 7: syntax error near unexpected token `done' "
I don't really know how to make it work!
Also even if I'm using #!/bin/bash I still have to explicitly call bash when trying to execute, otherwise it says that executing is not permitted, anybody knows why?
Thanks in advance :)
Couple of suggestions :
No harm double quoting variables
cp "$FILE" "$3" # prevents wordsplitting, helps you filenames with spaces
for statement fails for the fundamental reason -bad syntax- it should've been:
for FILE in ls "$1";
But then, never parse ls output. Check [ this ].
for FILE in ls "$1"; #drastic
Instead of the for-loop in step2 use a find-while-read combination:
find "$1" -type f -print0 | while read -rd'' filename #-type f for files
do
#something with $filename
done
Use lowercase variable names for your script as uppercase variables are reserved for the system. Check [this].
Use tools like [ shellcheck ] to improve script quality.
Edit
Since you have mentioned the input directories contain only files, my alternative approach would be
[[ -d "$1" && -d "$2" && -d "$3" ]] && for filename in "$1"/*
do
[ ! -e "$2/${filename##*/}" ] && cp "$filename" "$3"
done
If you are baffled by ${filename##*/} check [ shell parameter expansion ].
Sidenote: In linux, although discouraged it not uncommon to have non-standard filenames like file name.
Courtesy: #chepner & #mklement0 for their comments that greatly improved this answer :)
Your script:
if ...; then
for ...; do
if ...; then
...
done
else
...
fi
Fixed structure:
if ...; then
for ...; do
if ...; then
...
fi # <-- missing
done
else
...
fi
If you want the script executable, then make it so:
$ chmod +x script.sh
Notice that you also have other problems in you script. It is better written as
dir1="$1"
dir2="$2"
dir3="$3"
for f in "$dir1"/*; do
if [ ! -f "$dir2/$(basename "$f")" ]; then
cp "$f" "$dir3"
fi
done
this is not totally correct:
for FILE in $(ls $1); do
< whatever you do here >
done
There is a big problem with that loop if in that folder there is a filename like this: 'I am a filename with spaces.txt'.
Instead of that loop try this:
for FILE in "$1"/*; do
echo "$FILE"
done
Also you have to close every if statement with fi.
Another thing, if you are using BASH ( #!/usr/bin/env bash ), it is highly recommended to use double brackets in your test conditions:
if [[ test ]]; then
...
fi
For example:
$ a='foo bar'
$ if [[ $a == 'foo bar' ]]; then
> echo "it's ok"
> fi
it's ok
However, this:
$ if [ $a == 'foo bar' ]; then
> echo "it's ok";
> fi
bash: [: too many arguments
You've forgot fi after the innermost if.
Additionally, neither square brackets nor find do work this way. This one does what your script (as it is now) is intended to on my PC:
#!/bin/bash
if [[ -d "$1" && -d "$2" && -d "$3" ]] ; then
ls -1 "$1" | while read FILE ; do
ls "$2/$FILE" >/dev/null 2>&1 || cp "$1/$FILE" "$3"
done
else echo "Error: one or more directories are not present"
fi
Note that after a single run, when $2 and $3 refer to different directories, those files are still not present in $2, so next time you run the script they will be copied once more despite they already are present in $3.
I want to check the following condition, but it should be case insensitive.
if [ "SPP" == $1 ]
Is there anyway I can do it using regex.
You can also do the following:
#!/bin/bash
myParam=`echo "$1" | tr 'a-z' 'A-Z'`
if [ "SPP" == "$myParam" ]; then
echo "Is the same"
else
echo "It is not the same"
fi
This script will automatically converts user input to uppercase before making any string comparison. By doing so, you will not have to use regex for case insensitive string comparison.
Hope it helps.
Better late than never...
If that's ksh93, use the ~(i:...) case-insensitive globbing sub-pattern:
if [[ $1 == *~(i:spp)* ]]; then
: matched.
fi
For ksh88 (also the ksh clones), use an intermediary variable typeset -u'd to force upper-case:
typeset -u tocheck=$1
if [[ $tocheck == *SPP* ]]; then
: matched
fi
You can use:
shopt -s nocasematch
For case insensitive matching in BASH.
Alternatively this should also work:
[[ "$1" == [sS][pP][pP] ]]
I have a string
$VAR="I-UAT";
in my shell script code. I need a conditional statement to check if "UAT" is present in that string.
What command should I use to get either true or false boolean as output?
Or is there any other way of checking it?
What shell? Using bash:
if [[ "$VAR" =~ "UAT" ]]; then
echo "matched"
else
echo "didn't match"
fi
You can do it this way:
case "$VAR" in
*UAT*)
# code when var has UAT
;;
esac
The classic way, if you know ahead of time what string you're looking for, is a case statement:
case "$VAR" in
*UAT*) : OK;;
*) : Oops;;
esac
You can use an appropriate command in place of the : command. This will work with Bourne and Korn shells too, not just with Bash.
found=`echo $VAR | grep -c UAT`
Then test for $found non-zero.
In bash script you could use
if [ "$VAR" != "${VAR/UAT/}" ]; then
# UAT present in $VAR
fi
try with grep:
$ echo I\-UAT | grep UAT
$ echo $?
0
$ echo I\-UAT | grep UAX
$ echo $?
1
so testing
if [ $? -ne 0 ]; then
# not found
else
# found
fi
I like this a little better than using case/esac (and it'll work with non-bash shells):
#!/bin/sh
full_string="I-UAT"
substring="UAT"
if [ -z "${full_string##*$substring*}" ]; then
echo "Found substring!"
else
echo "Substring is MIA!"
fi
If the string returned is zero-length (-z), then the substring was found.