Getting command line argument that stores in a variable - bash

I am writing a bash script to finger the first three line of user's info.
ex:
$ ./c.sh bob unknown
Login: bob Name: Bob
Directory: /u1/h7/bob Shell: /bin/tcsh
Office: AA 044, x8361 Home Phone: 000-000-0000
unknown: no such user.
Here is my code so far
#!/bin/bash
if [ $# == 0 ]; then
echo "Usage: ./c.sh Login/Username"
exit
else
i=$#
j=1
while [ "$j" -le "$i" ]; do
finger ${$j} | head -n+3
echo
j=$(($j+1))
done
fi
instead of giving what user types for the command line arguments, ${$j} is giving me the the value of $j, any suggestion and help for how to get the login/username? I've tried $($j), $((j)), ${$j}....

The easy answer: stop using unnecessary indirection:
#!/bin/bash
if (( $# == 0 )); then
echo "Usage: ./c.sh Login/Username"
exit
else
while [[ $1 ]]; do
finger "$1" | head -n+3
echo
shift
done
fi
or…
…
for user; do # equivalent to `for user in "$#"; do`
finger "$user" | head -n+3
…
done
You could write it this way:
i=$#
j=1
while [ $j -le $i ]; do
finger "${#:j++:1}" | head -n+3
echo
done
…but you don't need to work that hard.

#!/bin/bash
if [[ $# -eq 0 ]]; then
echo "Usage: $0 Login/Username"
exit
else
for ARG in "$#"; do
finger "$ARG" | head -n 3
echo # If you want a newline
done
fi
As simple as it can be.

Related

Having difficulty writing a script to sort words

I am dealing with sorting words in Bash according to a given argument. I am given either argument -r, -a , -v or -h and according to it there are options to sort the words, as you can see at my "help".
Somehow, if I pass the argument -r it creates an error. I really don't understand what I am doing wrong, as if[["$arg"=="-a"]] works, but I have to use case somehow.
Here is my code:
#!/bin/bash
# Natalie Zubkova , zubkonat
# zubkonat#cvut.fel.cz , LS
#help
help="This script will calculate occurances of words in a given file, and it will sort them according to the given argument in following order> \n
without parametre = increasing order according to a number of occurance\n
-r = decreasing order according to a number of occurance\n
-a = in alphabetical increasing order\n
-a -r = in alphabetical decreasing order\n
There are also special cases of the given parametre, when the script is not sorting but:\n
-h = for obtaining help \n
-v = for obtaining a number of this task "
# this function will divide a given chain into a words, so we can start calculating the occurances, we also convert all the capital letters to the small ones by - tr
a=0;
r=0;
EXT=0;
if [ "$1" == "-h" ]; then
echo $help
exit 0
fi
if [ "$2" == "-h" ]; then
echo $help
exit 0
fi
if [ "$1" == "-v" ]; then
echo "5"
exit 0
fi
if [ "$2" == "-v" ]; then
echo "5"
exit 0
fi
function swap {
while read x y; do
echo "$y" "$x";
done
}
function clearAll {
sed -e 's/[^a-z]/\n/gI' | tr '[A-Z]' '[a-z]' | sort | uniq -c |awk '{i++; if(i!=1) print $2" "$1}' #swap
}
for arg do
case "$arg" in
"-a")
a=1
;;
"-r")
r=1
;;
"-v")
echo "5" #number of task is 5
exit 0
;;
"-h")
echo $help
exit 0
;;
"-?")
echo "invalid parametre, please display a help using argument h"
exit 0
;;
esac
done
#Sort according to parametres -a and -r
function sortWords {
if [[ a -eq 1 ]]; then
if [[ r -eq 0 ]]; then
clearAll | sort -nk1
fi
fi
if [[ a -eq 1 ]]; then
if [[ r -eq 1 ]]; then
clearAll | sort -nk1 -r
fi
fi
if [[ r -eq 1 ]]; then
if [[ a -eq 0 ]]; then
clearAll | sort -nk2 -r
fi
fi
if [[ a -eq 0 ]]; then
if [[ r -eq 0 ]]; then
clearAll | sort -nk2
fi
fi
}
#code is from Stackoverflow.com
function cat-all {
while IFS= read -r file
do
if [[ ! -z "$file" ]]; then
cat "$file"
fi
done
}
#histogram
hist=""
for arg do
if [[ ! -e "$arg" ]]; then
EXT=1;
echo "A FILE DOESNT EXIST" >&2
continue;
elif [[ ! -f "$arg" ]]; then
EXT=1;
echo "A FILE DOESNT EXIST" >&2
continue;
elif [[ ! -r "$arg" ]]; then
EXT=1;
echo "A FILE DOESNT EXIST" >&2
continue;
fi
done
for arg do
hist="$hist""$arg""\n"
done
echo -e "$hist" | cat-all | sortWords
exit $EXT;
Here is what our upload system which does some test to see if our program works says:
Test #6
> b5.sh -r ./easy.txt
ERROR: script output is wrong:
--- expected output
+++ script stdout
## --- line 1 (167 lines) ; +++ no lines ##
-the 89
-steam 46
-a 39
-of 37
-to 35
...
script written 484 lines, while 484 lines are expected
script error output:
A FILE DOESNT EXIST
cat: invalid option -- 'r'
Try `cat --help' for more information.
script exit value: 1
ERROR: Interrupted due to failed test
If anyone could help me I would really appreciate it.
You forgot to move the parameter index position with shift:
"-r")
r=1
shift
;;
shift above moves to the next command line arg: ./easy.txt in your case.
Without it, read -r file will read -r instead of the file name.

bash- reading file from stdin and arguments

So I have googled this and thought I found the answers, but it still doesnt work for me.
The program computes the average and median of rows and columns in a file of numbers...
Using the file name works:
./stats -columns test_file
Using cat does not work
cat test_file | ./stats -columns
I am not sure why it doesnt work
#file name was given
if [[ $# -eq 2 ]]
then
fileName=$2
#file name was not given
elif [[ $# -eq 1 ]]
then
#file name comes from the user
fileName=/dev/stdin
#incorrect number of arguments
else
echo "Usage: stats {-rows|-cols} [file]" 1>&2
exit 1
fi
A very simple program that accepts piped input:
#!/bin/sh
stdin(){
while IFS= read -r i
do printf "%s" "$i"
done
}
stdin
Test is as follows:
echo "This is piped output" | stdin
To put that into a script / utility similar to the one in the question you might do this:
#!/bin/sh
stdin(){
while IFS= read -r i
do printf "%s" "$i"
done
}
rowbool=0
colbool=0
for i in $#
do case "$i" in
-rows) echo "rows set"
rowbool=1
shift
;;
-cols) echo "cols set"
colbool=1
shift
;;
esac
done
if [[ $# -gt 0 ]]
then
fileName=$1
fi
if [[ $# -eq 0 ]]
then fileName=$(stdin)
fi
echo "$fileName"

shell script: "'<<' unmatched"-syntax error using here document

Hi I am attempting to write a program that will alert the user if a person of interest has come online at a given time. My program thus far is
#!/usr/bin/ksh
message=""
when=""
validFiles=""
validUsers=""
if [ $# -gt 0 ] ; then
while getopts w:m: opt
do
case $opt in
w) when=$OPTARG;;
m) message=$OPTARG;;
\?) echo $USAGE exit 2;;
esac
done
shift $(($OPTIND - 1))
if [[ $# -gt 0 ]] ; then
for i; do
if [[ -f "$i" && -r "$i" ]]; then
if ! echo $validFiles | grep $i >/dev/null; then
validFiles="$validFiles $i"
fi
elif id $i 2> /dev/null 1>&2; then
if ! echo $validUsers | grep $i > /dev/null; then
validUsers="$validUsers $i"
fi
fi
done
if [[ $when != "" && $validFiles != "" || $validUsers != "" ]] ;then
for i in $validUsers; do
if ! grep $i $validFiles >/dev/null; then
at $when <<"END"
if finger $i | grep $i; then
echo "$i is online" | elm $message
fi
END
fi
done
fi
else
echo "No files or usernames"
fi
else
echo "No arguments provided"
fi
My problem is that when I attempt to run this I get the error message
syntax error at line 33 : `<<' unmatched
I am not sure as to why this is appearing. I have checked many other examples and my at command,here document, appears to be the same as theirs. Could anybody help me out? Thanks.
The here string delimiter must not be indented, your END should be at the beginning of the line:
$ cat <<EOT
> foo
> bar
> EOT
foo
bar
If you want the trailing delimiter to be indented you can use the following syntax, but this will also strip all leading tabs from the here document itself (this only works with tabs!):
$ cat <<-EOT
> foo
> bar
> quux
> EOT
foo
bar
quux
Note that this behaviour is specified by POSIX so should work in all compliant shells:
If the redirection symbol is "<<-", all leading tabs shall be stripped from input lines and the line containing the trailing delimiter.

Autocomplete file name in a bash script

I'm trying to imitate the bash file completion.
Suppose I have the following files:
test1
test2
With an input string of "te" I would like to get the output "test"
This is my current attempt ($c is the input string):
l=1
q="$c"
for j in $(ls -A | grep "^$c"); do
if [ "${j/$c}" != "$j" ]; then
n=$(ls -A | grep ^$j | wc -l)
if [ $n -gt $l ]; then
q="$j"
fi
fi
done
c="$q"
echo $c
Thanks for any help
I tend to think there is no a way to get this from completion engine since it’s not a part of GNU Bash but Readline. But at least we can get list of possible completions with compgen. And an inmplementaion of finding longest common prefix should not be problem. So...
#!/bin/bash
SCRIPTNAME="${0##*/}"
USAGE="Usage: $SCRIPTNAME <prefix>
Print common prefix of possible file name completions. Like <TAB> but to
stdout."
(( $# == 1 )) || { printf >&2 '%s\n' "$USAGE"; exit 1; }
PREFIX="$1"
commonprefix() {
(( $# >= 2 )) || {
echo "$1"
return 0
}
local -i i N M
for ((i=0; i<=${#1}; i++)); do
for ((N=1; N<=$#-1; N++)); do
let M=$N+1
[[ ${!N:i:1} == ${!M:i:1} ]] || break 2
done
done
echo "${1:0:i}"
}
readarray -t COMPLETIONS < <(compgen -f "$PREFIX")
commonprefix "${COMPLETIONS[#]}"
Although Dmitry Alexandrov already provided a better solution, I still would like to post my own one which I made while waiting for the answers:
l=1
while [ -n $l ]; do
l=${#c}
a=$(ls -A | grep "^$c" | wc -l)
q=$c
for i in $(ls -A | grep "^$q"); do
if [ $i == $q ]; then
unset l
break
else
v=$(ls -A | grep "^$q${i:$l:1}" | wc -l)
if [ $v == $a ]; then
q="$c${i:$l:1}"
break
fi
fi
done
if [ $c == $q ]; then break; fi
c=$q
done
echo $c
It works with all of my tests, but it's slow (although it could be optimized).
Just to show that you were thinking in correct direction, I made your code work:
#!/bin/bash
c=$1
q="$c"
for j in $c*; do
if [ "${j/$c}" != "$j" ]; then
startn=$(ls -1A | grep -c "^${j:0:$((${#c} + 1))}")
for (( i=${#c}; i <= ${#j}; i++ )); do
n=$(ls -1A | grep -c "^${j:0:$i}")
if [ "$n" -lt "$startn" ]; then
q="${j:0:$((i - 1))}"
elif [ "$n" -le "$startn" ]; then
q="${j:0:$i}"
fi
done
fi
done
c="$q"
echo "$c"
But, it's just a proof of concept, don't use it. See answer by Dmitry Alexandrov for a good solution.

Logical Error in Shell script.Please help (UNIX)

Following is a source code which takes in only 'files',lists the file permissions of a file and prints the output by replacing
r=READ,w-WRITE,x-EXECUTABLE.
It should also echo "User".But the My problem here is that I have replaced '-' by User but then if the file has a permission of r--x,it also prints "User" # that point.I know its not a correct way to go about it.Can anyone suggest me a better way of echoing "User".
I have also tried printing it before the loop but then it won't serve my purpose, as My program only works withe file permissions of a FILE and not any block/socket/pipe/directory/etc.
#!/bin/bash
if [ $# -lt 1 ];then
echo "USAGE: $0 file-name"
exit 1
fi
ls -l $1 | cut -c1-4 | tr "\012" "." > fp
i=1
while(($i <= 4))
do
p=`cat fp | cut -c$i`
case $p in
[dbsplc] | t) echo "not a file";
exit 1;;
-) echo "User";;
r) echo "READ";;
w) echo "WRITE";;
x) echo "EXECUTE";;
esac
((++i))
done
exit 0
Too complicated. You don't have to rely on ls at all:
#!/bin/bash
if [[ $# -lt 1 ]]; then
echo "USAGE: $(basename "$0") filename ..."
exit 1
fi
exit_status=0
for file in "$#"; do
if [[ ! -f "$file" ]]; then
echo "not a file: $file" >&2
exit_status=$(( exit_status + 1 ))
continue
fi
echo "$file:"
echo "User"
[[ -r "$file" ]] && echo "READ"
[[ -w "$file" ]] && echo "WRITE"
[[ -x "$file" ]] && echo "EXECUTE"
done
exit $exit_status
I'd just use stat -c %a and process that instead.
an exemple using awk (easily adaptable to your program)
ll |awk '{
rights=substr($1, 2, 3);
sub(/r/, "READ ", rights);
sub(/w/, "WRITE ", rights);
sub(/x/, "EXECUTE ", rights);
print rights $3
}'
Explanations :
rights=substr($1, 2, 3);
$1 contains rights of your program and we only takes the 3 first rights (user one)
sub(/r/, "READ ", rights);
Substiture "r" with READ in rights (and so on).
print rights $3
Print rights (substituated) and $3 that contains the user name.
This served my purpose,I separated the first condition into a different case-statement.:
#!/bin/bash
if [ $# -lt 1 ];then
echo "USAGE: $0 file-name"
exit 1
fi
ls -l $1 | cut -c1-4 | tr "\012" "." > fp
i=1
while(($i == 1))
do
p=`cat fp | cut -c$i`
case $p in
[dbsplc] | t) echo "not a file";
exit 1;;
esac
echo "User"
((++i))
done
while(($i <= 4))
do
p=`cat fp | cut -c$i`
case $p in
r) echo "READ";;
w) echo "WRITE";;
x) echo "EXECUTE";;
esac
((++i))
done
exit 0

Resources