Passing variables in arguments - bash

Im looking how to pass an argument in a find file function
this should give all the sh files in my computer first the basename a tab with then all
the directories
For example myBashfile.sh *sh
at the moment i have this:
while getopts b opt
do
case $opt in
b) find / -name $OPTARG -printf "%f\n"-print 2>/dev/null ;;
esac
done
wich gives only the output of
test1.sh
test60.sh
anothertest.sh
but i need as output: (with a tab)
test1.sh /home/directory5/directory6
test60.sh /home/directory50/directory6
anothertest.sh /home/directory5/directory6
can anyone help me please?

A comprehensive answer and the manuals. In brief:
$1 # means the 1st argument
$2 # means the 2nd argument
$# # means all arguments

Related

Using wildcard and regex in find

I want to use find to list specific files using wildcard or regular expression.
For instance, I am currently using the following bash code to list all files starting with A.
nm="$2" # user passes wildcard "A*" in $nm.
find $dir -name $nm
But one problem is that I don't want Bash to expand the wildcard being passed by the user.
I constructed the following bash function
mse ()
{
local incl=()
local iarg=0 narg="$#"
IFSPREV="$IFS" # Save IFS (splits arguments on whitespace by default)
IFS=" |=" # Split arguments on " " and "="
set -- $* # Set positional parameters to command line arguments
IFS="$IFSPREV" # Set original IFS
while (( $# > 0 )); do
iarg=$(( iarg + 1 ))
case "$1" in
("--incl") incl+=("$2") ; echo "Test: $2" ; shift 2 ;;
esac
done
echo "\$#: $#"
echo "incl: ${incl[#]}"
}
Then I run the function in the terminal
mse -d /media/hag/hc1/a1-chaos/amvib/ --incl="A*"
which gives
incl: Admir
The result is wrong because it is picking up the directory name Admir that shows up it the current working directory.
And for passing regular expressions, how should the find command be constructed?
I suggest:
find "$dir" -name "$nm"
(Double) quotation marks prevent the expansion of wildcards.
To actually use regular expressions with find, pass the output of find to grep, for example:
find "$dir" | grep "$nm"
For example, I frequently use this variation:
find "$dir" | grep -P "$nm"
Here, GNU grep uses option:
-P : Use Perl regexes.
SEE ALSO:
grep manual
perlre - Perl regular expressions

List all the files with prefixes from a for loop using Bash

Here is a small[but complete] part of my bash script that finds and outputs all files in mydir if the have the prefix from a stored array. Strange thing I notice is that this script works perfectly if I take out the "-maxdepth 1 -name" from the script else it only gives me the files with the prefix of the first element in the array.
It would be of great help if someone explained this to me. Sorry in advance if there is some thing obviously silly that I'm doing. I'm relatively new to scripting.
#!/bin/sh
DIS_ARRAY=(A B C D)
echo "Array is : "
echo ${DIS_ARRAY[*]}
for dis in $DIS_ARRAY
do
IN_FILES=`find /mydir -maxdepth 1 -name "$dis*.xml"`
for file in $IN_FILES
do
echo $file
done
done
Output:
/mydir/Abc.xml
/mydir/Ab.xml
/mydir/Ac.xml
Expected Output:
/mydir/Abc.xml
/mydir/Ab.xml
/mydir/Ac.xml
/mydir/Bc.xml
/mydir/Cb.xml
/mydir/Dc.xml
The loop is broken either way. The reason why
IN_FILES=`find mydir -maxdepth 1 -name "$dis*.xml"`
works, whereas
IN_FILES=`find mydir "$dis*.xml"`
doesn't is because in the first one, you have specified -name. In the second one, find is listing all the files in mydir. If you change the second one to
IN_FILES=`find mydir -name "$dis*.xml"`
you will see that the loop isn't working.
As mentioned in the comments, the syntax that you are currently using $DIS_ARRAY will only give you the first element of the array.
Try changing your loop to this:
for dis in "${DIS_ARRAY[#]}"
The double quotes around the expansion aren't strictly necessary in your specific case, but required if the elements in your array contained spaces, as demonstrated in the following test:
#!/bin/bash
arr=("a a" "b b")
echo using '$arr'
for i in $arr; do echo $i; done
echo using '${arr[#]}'
for i in ${arr[#]}; do echo $i; done
echo using '"${arr[#]}"'
for i in "${arr[#]}"; do echo $i; done
output:
using $arr
a
a
using ${arr[#]}
a
a
b
b
using "${arr[#]}"
a a
b b
See this related question for further details.
#TomFenech's answer solves your problem, but let me suggest other improvements:
#!/usr/bin/env bash
DIS_ARRAY=(A B C D)
echo "Array is : "
echo ${DIS_ARRAY[*]}
for dis in "${DIS_ARRAY[#]}"
do
for file in "/mydir/$dis"*.xml
do
if [ -f "$file" ]; then
echo "$file"
fi
done
done
Your shebang line references sh, but your question is tagged bash - unless you need POSIX compliance, use a bash shebang line to take advantage of all that bash has to offer
To match files located directly in a given directory (i.e., if you don't need to traverse an entire subtree), use a glob (filename pattern) and rely on pathname expansion as in my code above - no need for find and command substitution.
Note that the wildcard char. * is UNquoted to ensure pathname expansion.
Caveat: if no matching files are found, the glob is left untouched (assuming the nullglob shell option is OFF, which it is by default), so the loop is entered once, with an invalid filename (the unexpanded glob) - hence the [ -f "$file" ] conditional to ensure that an actual match was found (as an aside: using bashisms, you could use [[ -f $file ]] instead).

How to set some variable to some value when option is present

I am not good at Bash scripting, and trying to learn more. Let me introduce my question with code:
#!/bin/bash
version_num=
isWindows=
MSVC_VER=
VERBOSE=
while getopts “hw:v:m:V” OPTION
do
case $OPTION in
h)
usage
exit 1
;;
w)
isWindows=$OPTARG
;;
v)
version_num=$OPTARG
;;
m)
MSVC_VER=$OPTARG
;;
V)
VERBOSE=1
;;
?)
usage
exit
;;
esac
done
For space, usage function is removed.
My questions are:
First question:
currently, if I use this script, I have to feed parameter values after each option, for example:
`bash test_bash.sh -v 4.2.2.0 -m 10.0 -w 1`
However, assuming that I only need to see whether -w is present, then set some variable value. I don't want to provide -w 1 since 1 is just a flag to do something. I would like the script to work like:
bash test_bash.sh -w -v 4.2.2.0 -m 10.0
How can I achieve this? I would like to do something like rm -r -f, which can have multiple options and does not require that each option is followed by some value.
Second question:
if I remove
V)
VERBOSE=1
;;
and :V from the while line as well as VERBOSE=, this script does not work anymore. Is it because :V is required?
Thanks a lot for your time and help!
Putting a : after a letter in the getopts parameter indicates whether it takes a parameter after it or not. So change to:
while getopts “hwv:m:V” OPTION
Removeing :V from the script breaks it because the : is for the m option that comes before it, not the V option that comes after. When you remove that :, it means that m no longer takes a parameter, but you need that.

Errors from if and else statements in shell

I am just new to programming in Unix and have a small issue that I am unsure of how to solve. The objective of this piece of my script is to offer the user various options as to the type of scan they would like to use. This scan detects duplicate files with specified variables depending on the option chosen.
I am unable to get it working at all and am unsure why?
Also could you please offer me advice on how I could better display the selection screen if possible. I have only pasted part of my code as I would like to figure out the rest of my objective myself.
#!/bin/bash
same_name="1"
filesize="2"
md5sum="3"
different_name="4"
echo "The list of choices are, same_name=1, filesize=2, md5sum=3 and different name=4"
echo "Search for files with the:"
read choice
if [$choice == "$same_name" ];then
find /home/user/OSN -type f -exec basename '{}' \; | sort > filelist.txt
find /home/user/OSN -type f -exec basename '{}' \; | sort | uniq -d > repeatlist.txt
else
ls -al /home/user/OSN >2filelist.txt
fi
The shell command [ also known as test needs a space after it for the shell to parse correctly. For example:
if [ "x$choice" == x"$same_name" ] ; then
is equivalent to
if test "x$choice" == "x$same_name" ; then
prepending "x" to the variables is an idiom to prevent test from seeing too few arguments. Test would complain if called as test 5 == so if $choice and $same_name were empty the call to expr is syntactically correct.
You can also use the construct ${choice:-default} or ${choice:=default} to guard against unset or null shell variables.
It would help if you included the error messages you were receiving. When I tried this, I got an error of:
./foo: line 9: [1: command not found
This makes the problem fairly clear. The [ operator in the if statement is, in Unix's "never use something complicated when some simple hack will work" style, just another program. (See ls /bin/[ for proof!) As such, it needs to be treated like any other program with command-line options; you separate it from its options with whitespace. Otherwise, bash will think that "[$choice", concatenated, is the name of a program to execute and will try to execute it. Thus, that line needs to be:
if [ $choice == "$same_name" ];then
After I changed that, it worked.
Also, as a style suggestion, I'd note that the case construct is a much easier way to write this code than using if statements, when you've got more than one test. And, as noted in other answers, you should put " marks around $choice, to guard against the case where the user input is empty or contains spaces -- $choice, unquoted, will expand to a list of zero or more tokens separated by whitespace, whereas "$choice" always expands to a single token.
can't believe nobody's picked up this error: if you use [ (or test), the operator for string equality is = not ==.
you can do it like this.
while true
do
cat <<EOF
The list of choices are:
1) same name
2) filesize
3) md5sum
4) different name
5) exit
EOF
read -r -p "Enter your choice: " choice
case "$choice" in
1)
find /home/user/OSN -type f -exec basename '{}' \; | sort > filelist.txt
find /home/user/OSN -type f -exec basename '{}' \; | sort | uniq -d > repeatlist.txt
5) exit;
*) ls -al /home/user/OSN >2filelist.txt
esac
done
Bash's double square brackets are much more forgiving of quoting and null or unset variables.
if [[ $choice == "$same_name" ]]; then
You should take a look at Bash's select and case statements:
choices="same_name filesize md5sum different_name exit"
PS3="Make a selection: " # this is the prompt that the select statement will display
select choice in $choices
do
case $choice in
same_name)
find ...
;;
filesize)
do_something
;;
.
.
.
exit)
break
;;
esac
done

getopts in bash programming

when i am using 'while getopts d:n: OPTION' - how do i know that i got only invalid option and if yes that there is an argument after it?
You know you have an invalid option because the return value is '?'.
Either:
You can't tell whether it had an argument because the option was invalid.
Or:
You have to look at the next argument and see whether it starts with a dash.
The heuristic in the 'Or' option is imperfect. With option bundling, I could write:
command -xbf/fidget
If the getopts option string is 'xf:', then the 'b' is the invalid option.
If the getopts option string is 'xbf:', then there would be an option 'f' and the option argument '/fidget' after the valid option 'b'.
If the getopts option string is 'xb:f:', then the option argument for 'b' would be 'f/fidget'.
I think the 'either' attitude is correct - you cannot tell.
Code fragment from a command called 'rcsunco' - to cancel RCS checkouts (and written at a time when I was reluctantly moving off SCCS):
remove=yes
keep=no
get=no
quiet=
while getopts gknqrV opt
do
case $opt in
V) echo "`basename $0 .sh`: RCSUNCO Version $Revision: 2.1 $ ($Date: 2002/08/03 07:41:00 $)" |
rcsmunger
exit 0;;
g) get=yes;;
k) keep=yes;;
n) remove=no;;
q) quiet=-q;;
r) remove=yes;;
*) echo "Usage: `basename $0 .sh` [-{n|g}][-{r|k}] file [...]" 1>&2
exit 1;;
esac
done
shift $(($OPTIND-1))
These days, I'd used the 'balanced parentheses' notation in the 'case':
(q) quiet=-q;;
Also note that I do not explicitly test which option is returned - I let the catchall '*' case deal. I also observe that the usage message is not complete (no '-V' or '-q' documented), and the code is old enough that I haven't added a '-h' for help option. The script 'rcsmunger' replaces '$Revision 2.1 $' with just '2.1', more like the way SCCS replaces '%I%' with '2.1'.
An invalid option will have a question mark ?... IIRC processing of the command line arguments processing stop if there's an question mark...

Resources