When I execute the following Windows batch script on Windows Server 2012:
#echo off
SET v=()
IF 1 == 2 (
echo hi
echo %v:~0%
echo 1 equals 2
) ELSE (
echo 1 does not equal 2
)
IF 1 == 2 (
echo %v:0%
echo 1 equals 2
) ELSE (
echo 1 does not equal 2
)
I get the following output:
1 equals 2
1 does not equal 2
1 does not equal 2
Can anybody tell me why this happens? I don't want to go into the block starting with echo hi even if the value of v is ().
I'm not quite sure what your intention is, but here's how I think your issue can be fixed.
The variable %v% is parsed before the IF command is run, and that contains a problematic closing parenthesis. What happens therefore is that the code reads, echo %v:~0% as echo ( and closes the IF with ) It then parses the next line which is echo 1 equals 2, and prints it as expected.
To prevent that, either escape that parenthesis, when you define that variable:
#echo off
SET "v=(^)"
IF 1 == 2 (
echo hi
echo %v:~0%
echo 1 equals 2
) ELSE (
echo 1 does not equal 2
)
IF 1 == 2 (
echo %v:0%
echo 1 equals 2
) ELSE (
echo 1 does not equal 2
)
Or, better still enable delayed expansion, so that the variable content is not parsed before the command is run, only when it is:
#echo off
SET "v=()"
setlocal enabledelayedexpansion
IF 1 == 2 (
echo hi
echo !v:~0!
echo 1 equals 2
) ELSE (
echo 1 does not equal 2
)
IF 1 == 2 (
echo !v:0!
echo 1 equals 2
) ELSE (
echo 1 does not equal 2
)
Related
Essentially I'm working on a project in command line, and I'm trying to have multiple if statements with multiple conditions to end up with something, in this case, echo displaying something. Here's an example:
if %piece%==1 if %haut%==1 (
if %couleur%==1 (
echo haut 1 1 1
) else if %couleur%==2 (
echo haut 1 1 2
))
if %piece%==1 if %haut%==2 (
if %couleur%==1 (
echo haut 1 2 1
) else if %couleur%==2 (
echo haut 1 2 2
))
Now this works fine, displaying what's shown beside echo, and when I add a second one, it also works fine, like so:
if %piece%==1 if %haut%==1 (
if %couleur%==1 (
echo haut 1 1 1
) else if %couleur%==2 (
echo haut 1 1 2
))
if %piece%==1 if %haut%==2 (
if %couleur%==1 (
echo haut 1 2 1
) else if %couleur%==2 (
echo haut 1 2 2
))
if %piece%==2 if %haut%==1 (
if %couleur%==1 (
echo haut 2 1 1
) else if %couleur%==2 (
echo haut 2 1 2
))
if %piece%==2 if %haut%==2 (
if %couleur%==1 (
echo haut 2 2 1
) else if %couleur%==2 (
echo haut 2 2 2
))
But once I add a third one which has a different second condition, instead of haut it's pantalon, it suddenly crashes the program instead of giving the answer.
if %piece%==1 if %haut%==1 (
if %couleur%==1 (
echo haut 1 1 1
) else if %couleur%==2 (
echo haut 1 1 2
))
if %piece%==1 if %haut%==2 (
if %couleur%==1 (
echo haut 1 2 1
) else if %couleur%==2 (
echo haut 1 2 2
))
if %piece%==2 if %haut%==1 (
if %couleur%==1 (
echo haut 2 1 1
) else if %couleur%==2 (
echo haut 2 1 2
))
if %piece%==2 if %haut%==2 (
if %couleur%==1 (
echo haut 2 2 1
) else if %couleur%==2 (
echo haut 2 2 2
))
if %piece%==3 if %pantalon%==1 (
if %couleur%==1 (
echo pantalon 3 1 1
) else if %couleur%==2 (
echo pantalon 3 1 2
))
if %piece%==3 if %pantalon%==2 (
if %couleur%==1 (
echo pantalon 3 2 1
) else if %couleur%==2 (
echo pantalon 3 2 2
))
I've also tried doing it without the else if, and just having many if statements stacked, but the same problem arises. I'll add the entire code at the end just in case it's needed, but I really don't know what the issue is here and why this is being caused. If someone could help me out, it'd be really appreciated!
Here's the full code:
#echo off
:menu
cls
echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
echo Quelle piece vestimentaire voulez-vous?
echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
echo -------
echo Hommes
echo -------
echo Haut (1)
echo.
echo Haut (2)
echo.
echo Pantalon (3)
echo.
echo -------
echo Femmes/Tous genres confondus
echo -------
echo Haut (4)
echo.
echo Jupe (5)
echo.
echo Pantalon (6)
echo.
echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
echo.
set /p piece=
if %piece%==1 goto haut
if %piece%==2 goto haut
if %piece%==3 goto pantalon
if %piece%==4 goto haut
if %piece%==5 goto jupe
if %piece%==6 goto pantalon
:haut
cls
echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
echo Quelle style d'haut voulez-vous?
echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
echo Haut (1)
echo.
echo Haut (2)
echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
set /p haut=
goto couleur
:pantalon
cls
echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
echo Quelle style de pantalon voulez-vous?
echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
echo Pantalon (1)
echo.
echo Pantalon (2)
echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
set /p pantalon=
goto couleur
:jupe
cls
echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
echo Quelle style de jupe voulez-vous?
echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
echo Jupe (1)
echo.
echo Jupe (2)
echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
set /p jupe=
goto couleur
:couleur
cls
echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
echo Quelle couleurs voulez-vous?
echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
echo Rouge et noir (1)
echo.
echo Blue et noir (2)
echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
echo.
set /p couleur=
:export
cls
if %piece%==1 if %haut%==1 (
if %couleur%==1 (
echo haut 1 1 1
) else if %couleur%==2 (
echo haut 1 1 2
))
if %piece%==1 if %haut%==2 (
if %couleur%==1 (
echo haut 1 2 1
) else if %couleur%==2 (
echo haut 1 2 2
))
if %piece%==2 if %haut%==1 (
if %couleur%==1 (
echo haut 2 1 1
) else if %couleur%==2 (
echo haut 2 1 2
))
if %piece%==2 if %haut%==2 (
if %couleur%==1 (
echo haut 2 2 1
) else if %couleur%==2 (
echo haut 2 2 2
))
if %piece%==3 if %pantalon%==1 (
if %couleur%==1 (
echo pantalon 3 1 1
) else if %couleur%==2 (
echo pantalon 3 1 2
))
if %piece%==3 if %pantalon%==2 (
if %couleur%==1 (
echo pantalon 3 2 1
) else if %couleur%==2 (
echo pantalon 3 2 2
))
if %piece%==4 if %haut%==1 (
if %couleur%==1 (
echo haut 4 1 1
) else if %couleur%==2 (
echo haut 4 1 2
))
if %piece%==4 if %haut%==2 (
if %couleur%==1 (
echo haut 4 2 1
) else if %couleur%==2 (
echo haut 4 2 2
))
if %piece%==5 if %jupe%==1 (
if %couleur%==1 (
echo haut 4 1 1
) else if %couleur%==2 (
echo haut 4 1 2
))
if %piece%==5 if %jupe%==2 (
if %couleur%==1 (
echo haut 4 2 1
) else if %couleur%==2 (
echo haut 4 2 2
))
pause>nul
goto menu
If there's any other information you need I'll be happy to give it!
I would advise that you do not use Set /P to receive expected single digit known input, and instead use the more appropriate and built-in choice.exe utility. This will guarantee that the end user can only provide input from your known list.
As for your if / else, structure, I have removed some unnecessary things, because if many of the options are not 1, they must by default, be 2, so there's no need to ask a second if.
#Echo Off
SetLocal EnableExtensions
:Menu
ClS
Set /A haut=pantalon=jupe=couleur=0
Echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Echo Quelle piece vestimentaire voulez-vous?
Echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Echo -------
Echo Hommes
Echo -------
Echo Haut (1)
Echo(
Echo Haut (2)
Echo(
Echo Pantalon (3)
Echo(
Echo -------
Echo Femmes/Tous genres confondus
Echo -------
Echo Haut (4)
Echo(
Echo Jupe (5)
Echo(
Echo Pantalon (6)
Echo(
Echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Echo(
%SystemRoot%\System32\choice.exe /C 123456
Set "piece=%ERRORLEVEL%"
If %piece%==1 GoTo Haut
If %piece%==2 GoTo Haut
If %piece%==3 GoTo Pantalon
If %piece%==4 GoTo Haut
If %piece%==5 GoTo Jupe
If %piece%==6 GoTo Pantalon
:Haut
ClS
Echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Echo Quelle style d'haut voulez-vous?
Echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Echo Haut (1)
Echo(
Echo Haut (2)
Echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
%SystemRoot%\System32\choice.exe /C 12
Set "haut=%ERRORLEVEL%"
GoTo Couleur
:Pantalon
ClS
Echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Echo Quelle style de pantalon voulez-vous?
Echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Echo Pantalon (1)
Echo(
Echo Pantalon (2)
Echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
%SystemRoot%\System32\choice.exe /C 12
Set "pantalon=%ERRORLEVEL%"
GoTo Couleur
:Jupe
ClS
Echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Echo Quelle style de jupe voulez-vous?
Echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Echo Jupe (1)
Echo(
Echo Jupe (2)
Echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
%SystemRoot%\System32\choice.exe /C 12
Set "jupe=%ERRORLEVEL%"
GoTo Couleur
:Couleur
ClS
Echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Echo Quelle couleurs voulez-vous?
Echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Echo Rouge et noir (1)
Echo(
Echo Blue et noir (2)
Echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Echo(
%SystemRoot%\System32\choice.exe /C 12
Set "couleur=%ERRORLEVEL%"
:Export
ClS
If %piece%==1 (
If %haut%==1 (
If %couleur%==1 (
Echo haut 1 1 1
) Else (
Echo haut 1 1 2
)
) Else (
If %couleur%==1 (
Echo haut 1 2 1
) Else (
Echo haut 1 2 2
)
)
)
If %piece%==2 (
If %haut%==1 (
If %couleur%==1 (
Echo haut 2 1 1
) Else (
Echo haut 2 1 2
)
) Else (
If %couleur%==1 (
Echo haut 2 2 1
) Else (
Echo haut 2 2 2
)
)
)
If %piece%==3 (
If %pantalon%==1 (
If %couleur%==1 (
Echo pantalon 3 1 1
) Else (
Echo pantalon 3 1 2
)
) Else (
If %couleur%==1 (
Echo pantalon 3 2 1
) Else (
Echo pantalon 3 2 2
)
)
)
If %piece%==4 (
If %haut%==1 (
If %couleur%==1 (
Echo haut 4 1 1
) Else (
Echo haut 4 1 2
)
) Else (
If %couleur%==1 (
Echo haut 4 2 1
) Else (
Echo haut 4 2 2
)
)
)
If %piece%==5 (
If %jupe%==1 (
If %couleur%==1 (
Echo jupe 5 1 1
) Else (
Echo jupe 5 1 2
)
) Else (
If %couleur%==1 (
Echo jupe 5 2 1
) Else (
Echo jupe 5 2 2
)
)
)
If %piece%==6 (
If %pantalon%==1 (
If %couleur%==1 (
Echo pantalon 6 1 1
) Else (
Echo pantalon 6 1 2
)
) Else (
If %couleur%==1 (
Echo pantalon 6 2 1
) Else (
Echo pantalon 6 2 2
)
)
)
Pause 1>NUL
GoTo Menu
Personally however, I'd perform the if section more like this:
If %piece%==1 If %haut%==1 (
If %couleur%==1 (Echo haut 1 1 1) Else Echo haut 1 1 2
) Else If %couleur%==1 (Echo haut 1 2 1) Else Echo haut 1 2 2
If %piece%==2 If %haut%==1 (
If %couleur%==1 (Echo haut 2 1 1) Else Echo haut 2 1 2
) Else If %couleur%==1 (Echo haut 2 2 1) Else Echo haut 2 2 2
If %piece%==3 If %pantalon%==1 (
If %couleur%==1 (Echo pantalon 3 1 1) Else Echo pantalon 3 1 2
) Else If %couleur%==1 (Echo pantalon 3 2 1) Else Echo pantalon 3 2 2
If %piece%==4 If %haut%==1 (
If %couleur%==1 (Echo haut 4 1 1) Else Echo haut 4 1 2
) Else If %couleur%==1 (Echo haut 4 2 1) Else Echo haut 4 2 2
If %piece%==5 If %jupe%==1 (
If %couleur%==1 (Echo jupe 5 1 1) Else Echo jupe 5 1 2
) Else If %couleur%==1 (Echo jupe 5 2 1) Else Echo jupe 5 2 2
If %piece%==6 If %pantalon%==1 (
If %couleur%==1 (Echo pantalon 6 1 1) Else Echo pantalon 6 1 2
) Else If %couleur%==1 (Echo pantalon 6 2 1) Else Echo pantalon 6 2 2
If all you are doing is really just echoing those values, I'd offer the following modified script, (without changing your screen layout).
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
:Menu
ClS
Set /A haut=pantalon=jupe=couleur=0
Echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Echo Quelle piece vestimentaire voulez-vous?
Echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Echo -------
Echo Hommes
Echo -------
Echo Haut (1)
Echo(
Echo Haut (2)
Echo(
Echo Pantalon (3)
Echo(
Echo -------
Echo Femmes/Tous genres confondus
Echo -------
Echo Haut (4)
Echo(
Echo Jupe (5)
Echo(
Echo Pantalon (6)
Echo(
Echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Echo(
%SystemRoot%\System32\choice.exe /C 123456
Set "piece=%ERRORLEVEL%"
If %piece%==1 GoTo Haut
If %piece%==2 GoTo Haut
If %piece%==3 GoTo Pantalon
If %piece%==4 GoTo Haut
If %piece%==5 GoTo Jupe
If %piece%==6 GoTo Pantalon
:Haut
ClS
Echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Echo Quelle style d'haut voulez-vous?
Echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Echo Haut (1)
Echo(
Echo Haut (2)
Echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
%SystemRoot%\System32\choice.exe /C 12
Set "haut=%ERRORLEVEL%"
GoTo Couleur
:Pantalon
ClS
Echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Echo Quelle style de pantalon voulez-vous?
Echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Echo Pantalon (1)
Echo(
Echo Pantalon (2)
Echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
%SystemRoot%\System32\choice.exe /C 12
Set "pantalon=%ERRORLEVEL%"
GoTo Couleur
:Jupe
ClS
Echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Echo Quelle style de jupe voulez-vous?
Echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Echo Jupe (1)
Echo(
Echo Jupe (2)
Echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
%SystemRoot%\System32\choice.exe /C 12
Set "jupe=%ERRORLEVEL%"
GoTo Couleur
:Couleur
ClS
Echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Echo Quelle couleurs voulez-vous?
Echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Echo Rouge et noir (1)
Echo(
Echo Blue et noir (2)
Echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Echo(
%SystemRoot%\System32\choice.exe /C 12
Set "couleur=%ERRORLEVEL%"
:Export
ClS
For %%G In (haut pantalon jupe) Do (SetLocal EnableDelayedExpansion
For %%H In (!%%G!) Do EndLocal & If %%H Gtr 0 Set "var=%%G" & Set "val=%%H")
Echo %var% %piece% %val% %couleur%
Pause 1> NUL
GoTo :Menu
#ECHO OFF
SETLOCAL
:menu
FOR %%b IN (haut pantalon jupe couleur) DO SET /a %%b=0
CALL :accepter piece "piece vestimentaire" 123456 2 "Hommes" "Femmes/Tous genres confondus" 3 "Haut" "Haut" "Pantalon" 3 "Haut" "Jupe" "Pantalon"
GOTO piece%piece%
:haut
:Piece1
:Piece2
:Piece4
CALL :accepter haut "style d'haut" 12 0 2 "Haut" "Haut"
goto couleur
:pantalon
:piece3
:piece6
CALL :accepter pantalon "style de pantalon" 12 0 2 "Pantalon" "Pantalon"
goto couleur
:jupe
:piece5
CALL :accepter jupe "style de jupe" 12 0 2 "Jupe" "Jupe"
goto couleur
:couleur
CALL :accepter couleur "couleurs" 12 0 2 "Rouge et noir" "Blue et noir"
:export
cls
ECHO haut %haut% pantalon %pantalon% jupe %jupe% couleur %couleur%
pause>nul
goto menu
:: receive user input
:: %1 : receiving variable
:: %2 : Menu prompt
:: %3 : options list
:: %4 : number of groups
:: %5 : name of group (repeats %4 times)
:: %then : number of options within group
:: %followedby : Option name
:: %then, %followedby may be repeated
:accepter
cls
SETLOCAL ENABLEDELAYEDEXPANSION
:: remove variables starting groupname
For %%b IN (groupname) DO FOR /F "delims==" %%a In ('set %%b 2^>Nul') DO SET "%%a="
SET "value=%1"
shift
SET "menuprompt=%~1"
shift
SET "options=%1"
SHIFT
SET "groups=%1"
SHIFT
SET /a groupcount=%groups%
SET /a groupindex=0
:getgroupname
IF %groupcount% neq 0 SET /a groupindex+=1&SET "groupname[!groupindex!]=%~1"&SHIFT&IF !groupindex! neq %groupcount% GOTO getgroupname
SET groupname
::
SET /a groupindex=0
SET /a option=0
SET "unusedoptions=%options%"
::
echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
echo Quelle %menuprompt% voulez-vous?
echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:: Display group
:displaygroup
SET /a groupindex+=1
IF DEFINED groupname[%groupindex%] (
echo -------
echo !groupname[%groupindex%]!
echo -------
)
SET numoptionsingroup=%1&SHIFT
:showoption
IF %numoptionsingroup% gtr 0 (
ECHO %~1 (!unusedoptions:~0,1!^)
ECHO.
SET "unusedoptions=!unusedoptions:~1!"
SHIFT
SET /a numoptionsingroup-=1
GOTO showoption
)
IF DEFINED unusedoptions GOTO displaygroup
echo.
echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
echo.
choice /c %options% /M "%menuprompt%"
endlocal&SET %value%=%errorlevel%
GOTO :eof
I want to just insert number between two values, and otherwise the script repeated until correct number.
This is my script and it does not work correctly:
validation(){
read number
if [ $number -ge 2 && $number -ls 5 ]; then
echo "valid number"
break
else
echo "not valid number, try again"
fi
}
echo "insert number"
validation
echo "your number is" $number
If you are using Bash, you are better off using the arithmetic expression, ((...)) for readability and flexibility:
if ((number >= 2 && number <= 5)); then
# your code
fi
To read in a loop until a valid number is entered:
#!/bin/bash
while :; do
read -p "Enter a number between 2 and 5: " number
[[ $number =~ ^[0-9]+$ ]] || { echo "Enter a valid number"; continue; }
if ((number >= 2 && number <= 5)); then
echo "valid number"
break
else
echo "number out of range, try again"
fi
done
((number >= 2 && number <= 5)) can also be written as ((2 <= number <= 5)).
See also:
Test whether string is a valid integer
How to use double or single brackets, parentheses, curly braces
Your if statement:
if [ $number -ge 2 && $number -ls 5 ]; then
should be:
if [ "$number" -ge 2 ] && [ "$number" -le 5 ]; then
Changes made:
Quoting variables is considered good practice.
ls is not a valid comparison operator, use le.
Separate single-bracket conditional expressions with &&.
Also you need a shebang in the first line of your script: #!/usr/bin/env bash
if [ $number -ge 2 && $number -ls 5 ]; then
should be
if [[ $number -ge 2 && $number -le 5 ]]; then
see help [[ for details
Try bellow code
echo "Enter number"
read input
if [[ $input ]] && [ $input -eq $input 2>/dev/null ]
then
if ((input >= 1 && input <= 4)); then
echo "Access Granted..."
break
else
echo "Wrong code"
fi
else
echo "$input is not an integer or not defined"
fi
2 changes needed.
Suggested by Sergio.
if [ "$number" -ge 2 ] && [ "$number" -le 5 ]; then
There is no need of break. only meaningful in a for, while, or until loop
while :; do
read option
if [[ $option -ge 1 && $option -lt 4 ]]; then
echo "correct"
c
break
else
echo "Incorrect option selected,choose an option between [1-4]"
fi
done
I am trying to test that an infinite number of arguments ( "$#" ) to a bash script are numbers ( "#", "#.#", ".#", "#.") delimited by spaces (i.e. # # # # ...). I have tried:
[ "$#" -eq "$#" ]
similar to what I found in this answer but I get:
"[: too many arguments"
and I have also tried regular expressions but it seems once the regular expression is satisfied anything can come afterwards. here is my code:
if (($# >=1)) && [[ "$#" =~ ^-?[[:digit:]]*\.?[[:digit:]]+ ]]; then
it also needs to not allow "#.." or "..#"
I don't think that [ "$#" -eq "$#"] is going to work somehow.
A loop like this could help to read each argument and detect if it is an integer number (bash does not handle decimals):
for i in $#;do
if [ "$i" -eq "$i" ] 2>/dev/null
then
echo "$i is an integer !!"
else
echo "ERROR: not an integer."
fi
done
In your case , to determine if argument is a valid integer/decimal number instead of all those regex ifs, we can simply divide the number with it's self using bc program of bash.
If it is a valid number will return 1.00
So in your case this should work:
for i in $#;do
if [[ "$(bc <<< "scale=2; $i/$i")" == "1.00" ]] 2>/dev/null;then
echo "$i is a number and thus is accepted"
else
echo "Argument $i not accepted"
fi
done
Output:
root#debian:# ./bashtest.sh 1 3 5.3 0.31 23. .3 ..2 8..
1 is a number and thus is accepted
3 is a number and thus is accepted
5.3 is a number and thus is accepted
0.31 is a number and thus is accepted
23. is a number and thus is accepted
.3 is a number and thus is accepted
Argument ..2 not accepted
Argument 8.. not accepted
$# is an array of strings. You probably want to process the strings one at a time, not all together.
for i; do
if [[ $i =~ ^-?[[:digit:]]+\.?[[:digit:]]*$ ]] || [[ $i =~ ^-?\.?[[:digit:]]+$ ]]; then
echo yes - $i
else
echo no - $i
fi
done
In bash there is pattern matching with multiplier syntax that can help your problem. Here is a script to validate all arguments:
for ARG ; do
[[ "$ARG" = +([0-9]) ]] && echo "$ARG is integer number" && continue
[[ "$ARG" = +([0-9]).*([0-9]) ]] && echo "$ARG is float number" && continue
[[ "$ARG" = *([0-9]).+([0-9]) ]] && echo "$ARG is float number" && continue
[[ "$ARG" = -+([0-9]) ]] && echo "$ARG is negative integer number" && continue
[[ "$ARG" = -+([0-9]).*([0-9]) ]] && echo "$ARG is negative float number" && continue
[[ "$ARG" = -*([0-9]).+([0-9]) ]] && echo "$ARG is negative float number" && continue
echo "$ARG is not a number."
done
The for loop automatically uses the arguments received by the script to load the variable ARG.
Each test from the loop compares the value of the variable with a pattern [0-9] multiplied with + or * (+ is 1 or more , * is zero or more), sometimes there are multiple pattern next to each other.
Here is an example usage with output:
$ ./script.sh 123 -123 1.23 -12.3 1. -12. .12 -.12 . -. 1a a1 a 12345.6789 11..11 11.11.11
123 is integer number
-123 is negative integer number
1.23 is float number
-12.3 is negative float number
1. is float number
-12. is negative float number
.12 is float number
-.12 is negative float number
. is not a number.
-. is not a number.
1a is not a number.
a1 is not a number.
a is not a number.
12345.6789 is float number
11..11 is not a number.
11.11.11 is not a number.
I shall assume that you meant a decimal number, limited to either integers or floating numbers from countries that use a dot to mean decimal point. And such country does not use a grouping character (1,123,456.00125).
Not including: scientific (3e+4), hex (0x22), octal (\033 or 033), other bases (32#wer) nor arithmetic expressions (2+2, 9/7, 9**3, etc).
In that case, the number should use only digits, one (optional) sign and one (optional) dot.
This regex checks most of the above:
regex='^([+-]?)([0]*)(([1-9][0-9]*([.][0-9]+)?)|([.][0-9]+))$'
In words:
An optional sign (+ or -)
Followed by any amount of optional zeros.
Followed by either (…|…|…)
A digit [1-9] followed by zero or more digits [0-9] (optionally) followed by a dot and digits.
No digits followed by a dot followed by one or more digits.
Like this (since you tagged the question as bash):
regex='^([+-]?)([0]*)(([1-9][0-9]*([.][0-9]+)?)|([.][0-9]+))$'
[[ $n =~ $regex ]] || { echo "A $n is invalid" >&2; }
This will accept 0.0, and .0 as valid but not 0. nor 0.
Of course, that should be done in a loop, like this:
regex='^([+-]?)([0]*)(([1-9][0-9]*([.][0-9]+)?)|([.][0-9]+))$'
for n
do m=${n//[^0-9.+-]} # Only keep digits, dots and sign.
[[ $n != "$m" ]] &&
{ echo "Incorrect characters in $n." >&2; continue; }
[[ $m =~ $regex ]] ||
{ echo "A $n is invalid" >&2; continue; }
printf '%s\n' "${BASH_REMATCH[1]}${BASH_REMATCH[3]}"
done
I have written a small perl "hack" to replace 1's with alphabets in a range of columns in a tab delimited file. The file looks like this:
Chr Start End Name Score Strand Donor Acceptor Merged_Transcript Gencode Colon Heart Kidney Liver Lung Stomach
chr10 100177483 100177931 . . - 1 1 1 1 1 0 1 1 0 0
chr10 100178014 100179801 . . - 1 1 1 1 1 1 1 1 1 0
chr10 100179915 100182125 . . - 1 1 1 1 1 1 1 0 1 0
chr10 100182270 100183359 . . - 1 1 1 1 0 0 1 0 1 0
chr10 100183644 100184069 . . - 1 1 1 1 0 0 1 0 1 0
The gola is to take columns 11 through 16 and append letters A to Z if a value of 1 is seen in those columns. My code so far is producing an empty output and this is my first time doing regular expressions.
cat infile.txt \
| perl -ne '#alphabet=("A".."Z");
$is_known_intron = 0;
$is_known_donor = 1;
$is_known_acceptor = 1;
chomp;
$_ =~ s/^\s+//;
#d = split /\s+/, $_;
#d_bool=#d[$11-$16];
$ct=1;
$known_intron = $d[$10];
$num_of_overlapping_gene = $d[$9];
$known_acceptor = $d[$8];
$known_donor = $d[$7];
$k="";
if (($known_intron == $is_known_intron) and ($known_donor == $is_known_donor) and ($known_acceptor == $is_known_acceptor)) {
for ($i = 0; $i < scalar #d_bool; $i++){
$k.=$alphabet[$i] if ($d_bool[$i])
}
$alphabet_ct{$k}+=$ct;
}
END
{
foreach $k (sort keys %alphabet_ct){
print join("\t", $k, $alphabet_ct{$k}), "\n";
}
} '\
> Outfile.txt
What should I be doing instead?
Thanks!
* Edit *
Expected Output
ABCD 45
BCD 23
ABCDEF 1215
so on and so forth.
I converted your code into a script for ease of debugging. I've put comments in the code to point out dodgy bits:
use strict;
use warnings;
my %alphabet_ct;
my #alphabet = ( "A" .. "Z" );
my $is_known_intron = 0;
my $is_known_donor = 1;
my $is_known_acceptor = 1;
while (<DATA>) {
# don't process the first line
next unless /chr10/;
chomp;
# this should remove whitespace at the beginning of the line but is doing nothing as there is none
$_ =~ s/^\s+//;
my #d = split /\s+/, $_;
# the range operator in perl is .. (not "-")
my #d_bool = #d[ 10 .. 15 ];
my $known_intron = $d[9];
my $known_acceptor = $d[7];
my $known_donor = $d[6];
my $k = "";
# this expression is false for all the data in the sample you provided as
# $is_known_intron is set to 0
if ( ( $known_intron == $is_known_intron )
and ( $known_donor == $is_known_donor )
and ( $known_acceptor == $is_known_acceptor ) )
{
for ( my $i = 0; $i < scalar #d_bool; $i++ ) {
$k .= $alphabet[$i] if $d_bool[$i];
}
# it is more idiomatic to write $alphabet_ct{$k}++;
# $alphabet_ct{$k} += $ct;
$alphabet_ct{$k}++;
}
}
foreach my $k ( sort keys %alphabet_ct ) {
print join( "\t", $k, $alphabet_ct{$k} ) . "\n";
}
__DATA__
Chr Start End Name Score Strand Donor Acceptor Merged_Transcript Gencode Colon Heart Kidney Liver Lung Stomach
chr10 100177483 100177931 . . - 1 1 1 1 1 0 1 1 0 0
chr10 100178014 100179801 . . - 1 1 1 1 1 1 1 1 1 0
chr10 100179915 100182125 . . - 1 1 1 1 1 1 1 0 1 0
chr10 100182270 100183359 . . - 1 1 1 1 0 0 1 0 1 0
chr10 100183644 100184069 . . - 1 1 1 1 0 0 1 0 1 0
With $is_known_intron set to 1, the sample data gives the results:
ABCDE 1
ABCE 1
ACD 1
CE 2
I have a text file in the following format:
variableStep chrom=chr1 span=10
10161 1
10171 1
10181 2
10191 2
10201 2
10211 2
10221 2
10231 2
10241 2
10251 1
variableStep chrom=chr10 span=10
70711 1
70721 2
70731 2
70741 2
70751 2
70761 2
70771 2
70781 2
70791 1
71161 1
71171 1
71181 1
variableStep chrom=chr11 span=10
104731 1
104741 1
104751 1
104761 1
104771 1
104781 1
104791 1
104801 1
128711 1
128721 1
128731 1
I need a way to break this down into several files named for example "chr1.txt", "chr10.txt and "chr11.txt". How would I go about doing this?
I about the the following way:
cat file.txt | \
while IFS=$'\t' read -r -a rowArray; do
echo -e "${rowArray[0]}\t${rowArray[1]}\t${rowArray[2]}"
done > $file.mod.txt
That reads line by line and then saves line by line. However, I need something a little more elaborate that spans rows. "chr1.txt" would include everything from the row 10161 1 to row 10251 1, "chr10.txt" would include everything from the row 70711 1 to row 71181 1, etc. It's also specific in that I have to read in the actual chr# from each line as well, and save that as the file name.
The help is really appreciated.
awk -F'[ =]' '
$1 == "variableStep" {file = $3 ".txt"; next}
file != "" {print > file}' < input.txt
This worked for me:
IFS=$'\n'
curfile=""
content=($(< file.txt))
for ((idx = 0; idx < ${#content[#]}; idx++)); do
if [[ ${content[idx]} =~ ^.*chrom=(\\b.*?\\b)\ .*$ ]]; then
curfile="${BASH_REMATCH[1]}.txt"
rm -rf ${curfile}
elif [ -n "${curfile}" ]; then
echo ${content[idx]} >> ${curfile}
fi
done
Awk is appropriate for this problem domain because the text file is already (more or less) organized into columns. Here's what I would use:
awk 'NF == 3 && index($2, "=") { filename = substr($2, index($2, "=") + 1) }
NF == 2 && filename { print $0 > (filename ".txt") }' < input.txt
Explanation:
Think of the lines starting with variableStep as "three columns" and the other lines as "two columns". The above script says, "Parse the text file line-by-line; if a line has three columns and the second column contains an '=' character, assign 'all of the characters in the second column that occur after the '=' character' to a variable called filename. If a line has two columns and the filename variable's been assigned, write the entire line to the file that's constructed by concatenating the string in the filename variable with '.txt'".
Notes:
NF is a built-in variable in Awk that represents the "number of fields", where a "field" (in this case) can be thought of as a column of data.
$0 and $2 are built-in variables that represent the entire line and the second column of data, respectively. ($1 represents the first column, $3 represents the third column, etc...)
substr and index are built-in functions described here: http://www.gnu.org/software/gawk/manual/gawk.html#String-Functions
The redirection operator (>) acts differently in Awk than it does in a shell script; subsequent writes to the same file are appended.
String concatenation is performed simply by writing expressions next to each other. The parenthesis ensure the concatenation happens before the file gets written to.
More details can be found here: http://www.gnu.org/software/gawk/manual/gawk.html#Two-Rules
i used sed to filter ....
code part :
Kaizen ~/so_test $ cat zsplit.sh
cntr=1;
prev=1;
for curr in `cat ztmpfile2.txt | nl | grep variableStep | tr -s " " | cut -d" " -f2 | sed -n 's/variableStep//p'`
do
sed -n "$prev,$(( ${curr} - 1))p" ztmpfile2.txt > zchap$cntr.txt ;
#echo "displaying : : zchap$cntr.txt " ;
#cat zchap$cntr.txt ;
prev=$curr; cntr=$(( $cntr + 1 ));
done
sed -n "$prev,$ p" ztmpfile2.txt > zchap$cntr.txt ;
#echo "displaying : : zchap$cntr.txt " ;
#cat zchap$cntr.txt ;
output :
Kaizen ~/so_test $ ./zsplit.sh
+ ./zsplit.sh
zchap1.txt :: 1 :: 1
displaying : : zchap1.txt
variableStep chrom=chr1 span=10
zchap2.txt :: 1 :: 12
displaying : : zchap2.txt
variableStep chrom=chr1 span=10
10161 1
10171 1
10181 2
10191 2
10201 2
10211 2
10221 2
10231 2
10241 2
10251 1
zchap3.txt :: 12 :: 25
displaying : : zchap3.txt
variableStep chrom=chr10 span=10
70711 1
70721 2
70731 2
70741 2
70751 2
70761 2
70771 2
70781 2
70791 1
71161 1
71171 1
71181 1
displaying : : zchap4.txt
variableStep chrom=chr11 span=10
104731 1
104741 1
104751 1
104761 1
104771 1
104781 1
104791 1
104801 1
128711 1
128721 1
128731 1
from the result zchap* files , iff you want you can remove the line : variableStep chrom=chr11 span=10 by using sed -- sed -i '/variableStep/d' zchap*
does this help ?