linux - "read -p" numbers [0-9] and "_" as imput - bash

This time Bash question.
I am doing a script that will require user input "read -p".
The problem is that file name is in that format: vNN_NN_nn_nn_nn.zip. For example v3_11_1_2.zip or v_1_1_1_1_22.zip
I can add into script "v" to be up front - no problem.
The problem is how to read only [0-9] and [_] as a input parameter.
I tried all combinations and no success.
read -p "Please enter file name like 1_2_03_04: " filename
if [ ! $filename =~ ^[0-9]+$ ] || [! $zip =~ ^[_]+$ ]; then
echo "Filename is not valid"
else
echo "filename is $filename"
fi
No success so far. How that can be done?
Thank you.

There are a couple of changes I would make. First, be sure to use [[...]] rather than [...], as the former is BASH, and the later is just sh. The regex syntax is not just sh.
Next, you'll want to include both checks in the same regex. The [...] groups in regex can contain all the characters that you want to include.
Try this solution:
read -p "Please enter file name like 1_2_03_04: " filename
if [[ ! $filename =~ ^[0-9_]+$ ]] ; then
echo "Filename is not valid"
else
echo "filename is $filename"
fi

To support your format of filenames, I suggest using this regex:
re='^v[0-9]*(_[0-9]+)+$'
read -p "Please enter file name like v1_2_03_04: " file
[[ $file =~ $re ]] && echo "$file is valid" || echo "$file is not valid"
This will not allow weird looking filenames such as v____ or v12____ or v12_1_
RegEx Demo

Related

Bash - Comparing a string to an array that contains wildcards?

I have an array of possible file extensions, which contains some wild cards e.g.:
FILETYPES=("DBG" "MSG" "OUT" "output*.txt")
I also have a list of files, which I am grabbing the file extension from. I then need to compare the extension with the array of file extensions.
I have tried:
if [[ ${EXTENSION} =~ "${FILETYPES[*]}" ]]; then
echo "file found"
fi
if [[ ${EXTENSION} == "${FILETYPES[*]}" ]]; then
echo "file found"
fi
and
if [[ ${EXTENSION} =~ "${FILETYPES[*]}" ]]; then
echo "file found"
fi
But to no avail
I tried:
if [[ "${FILETYPES[*]}" =~ ${EXTENSION} ]]; then
echo "file found"
fi
However, it ended up comparing "txt" to "output*.txt" and concluding it was a match.
FILETYPES=("DBG" "MSG" "OUT" "output*.txt") First of all, avoid ALL_CAPS variable names except if these are meant as global environment variables.
"output*.txt": is ok as a globing pattern, for bash test [[ $variable == output*.txt ]] for example. But for Regex matching it needs a different syntax like [[ $variable =~ output.*\.txt ]]
"${FILETYPES[*]}": Expanding this array into a single_string was mostly a good approach, but it needs clever use of the IFS environment variable to help it expands into a Regex. Something like IFS='|' regex_fragment="(${array[*]})", so that each array entry will be expanded, separated by a pipe | and enclosed in parenthesis as (entry1|entry2|...).
Here is an implementation you could use:
textscript.sh
#!/usr/bin/env bash
extensions_regexes=("DBG" "MSG" "OUT" "output.*\.txt")
# Expands the extensions regexes into a proper regex string
IFS='|' regex=".*\.(${extensions_regexes[*]})"
# Prints the regex for debug purposes
printf %s\\n "$regex"
# Iterate all filenames passed as argument to the script
for filename; do
# Compare the filename with the regex
if [[ $filename =~ $regex ]]; then
printf 'file found: %s \n' "$filename"
fi
done
Sample usage:
$ touch foobar.MSG foobar.output.txt
$ bash testscript.sh *
.*\.(DBG|MSG|OUT|output.*\.txt)
file found: foobar.MSG
file found: foobar.output.txt
You cannot directly compare a string with an array. Would you please try something like:
filetypes=("DBG" "MSG" "OUT" "output*.txt")
extension="MSG" # example
match=0
for type in "${filetypes[#]}"; do
if [[ $extension = $type ]]; then
match=1
break
fi
done
echo "$match"
You can save looping with regex:
pat="^(DBG|MSG|OUT|output.*\.txt)$"
extension="output_foo.txt" # example
match=0
if [[ $extension =~ $pat ]]; then
match=1
fi
echo "$match"
Please note the expressions of regex differ from wildcards for globbing.
As a side note, we conventionally do not use uppercases for user variables to avoid conflicts with system variables.

Does glob work differently with [[ ]] vs [ ] in Bash?

I have the file $HOME/foo/foo.mov
if [[ -f $HOME/**/*.mov ]]; then
echo "file is there" else
echo "file is not there"
fi
echoes "file is not there". whereas,
if [ -f $HOME/**/*.mov ]]; then
echo "file is there" else
echo "file is not there"
fi
echoes "file there".
Why is the difference between [[ ]] and [ ]?
Arguments to the [ command are subject to pathname expansion; the words used inside [[ ... ]] are not.
If you are getting file is there, I suspect you have exactly one file that matches the pattern. Otherwise, you would have too many arguments for -f, or the pattern would be treated as a literal string that does not name an existing file.

Always gives false even though file exists and is not empty

I have a bash script:
echo " enter file name "
read $file
if [ -f "$file" ] && [ -s "$file" ]
then
echo " file does not exist, or is empty "
else
echo " file exists and is not empty "
fi
No matter what I enter as a $file, it gives me the false value. I can even enter a file that does not even exist; it still will give me the false value. Why is that?
It is enough to check for -s, because it says:
FILE exists and has a size greater than zero
http://unixhelp.ed.ac.uk/CGI/man-cgi?test
also your output is switched, so it outputs does not exists when a file exists, because -s will give TRUE if file exists AND has a size > 0.
So correctly you should use:
echo " enter file name "
read file
if [ -s "$file" ]
then
echo " file exists and is not empty "
else
echo " file does not exist, or is empty "
fi
This will give you the expected output.
Also it should be
read file
instead of
read $file
If you want further informations, I recommand reading man test and man read
Please note, that [ -f "$file" ] && [ -s "$file" ] will return true if file exists and is not empty.
Other option:
if [[ -f "/path/to/file" && -s "/path/to/file" ]]; then
echo "exist and not empty"
else
echo "not exist or empty";
fi
This is the true solution:
if [[ -f $file && -s $file ]]
With [[ the quotes are unnecessary because [[ handles empty strings and strings with whitespace more intuitively.
The solution that was proposed to you:
if [ -s "$file" ]
is wrong because it is instead equivalent to:
if [[ -e $file && -s $file ]]
which, in addition to the regular files dictated by the word -f, also looks for:
Directory
Symbolic Link
Block Special Device
Character Device
Unix Socket (local domain socket)
Named Pipe

How to use -f file existence comparison operator in echo in shell script?

#/bin/sh
echo "Please enter file name:"
read FILENAME
d=[ -f $FILENAME ]
echo $d
This doesn't work. -f: command not found. Okay sounds correct there.
Another variation that seems to be working, but instead in the console a newline is added.
#/bin/sh
echo "Please enter file name:"
read FILENAME
d=$(test -f $FILENAME )
echo $d
But I tried to test the value of $d and it is neither \n nor "0" nor 1.
You don't see any output because $d is empty, so you are effectively echoing an empty string (with the trailing newline added by echo). $d is empty because test doesn't return any output to be assigned to it. Instead when you work with test, the important thing is the exit status, which you can capture with $?:
echo "Please enter file name:"
read FILENAME
test -f $FILENAME
echo $?
Alternatively you can use this as an expression in other conditions:
read -p "Please enter file name: " FILENAME
[[ -f "$FILENAME" ]] && echo "This is a valid file" || echo "Not a valid file"

How to check if string begins with a square bracked in bash

I need to go through my ini file and check if the line begins with a bracket, to figure out that the new config has started. Is there a way of checking this in bash? I tried
line="[test]"
if [[ "$line" =~ [.* ]]; then
echo "Got it!"
else
echo "Nothing found"
fi
but it doesn't work for me. I presume that the bracket needs to be somehow escaped, but I can't find any info as to how. Any help will be much appreciated.
To do this, you should backslash the special character [, so :
line='[test]'
if [[ $line =~ ^\[ ]]; then
echo "Got it!"
else
echo "Nothing found"
fi
EXPLANATIONS
[ is the starting character to opening a REGEX class, like [0-9]
quotes are needed everywhere but not inside bash [[ ]] tests
It's terrible to use a regexp for that! please use a bash glob instead:
if [[ $line = [* ]]; then
echo 'Got it!'
else
echo "Nothing found"
fi
Hope this helps use bash more efficiently.

Resources