How to compare user defined variable to string literal - sh

I'm trying to write a script to allow users to input a filename and display either the number of lines, words, characters, or all three, that are in the file depending on whether the user enters 'l' (lines), 'w' (words), 'c' (characters), or 'a' (all).
Here's what I have so far:
#!/bin/sh
# Prompt for filename
read -p 'Enter the file name: ' filename
# Prompt which of lines, words, or chars to display
read -p 'Count lines, words, characters, or all three (l, w, c, a)? ' display
while [ $display -ne "l" -o $display -ne "w" -o $display -ne "c" -o $display -ne "a" ]
do
echo "Invalid option"
read -p 'Count lines, words, characters, or all three (l, w, c, a)? ' display
done
# Display to stdout number of lines, words, or chars
set `wc $filename`
if [ $display -eq "l" ]
then
echo "File '$4' contains $1 lines."
elif [ $display -eq "w" ]
then
echo "File '$4' contains $2 words."
elif [ $display -eq "c" ]
then
echo "File '$4' contains $3 characters."
else
echo "File '$4' contains $1 lines, $2 words, and $3 characters."
fi
If I run the script and supply a file called trial.txt and choose option w, I getting the output:
./icount: 11: [: Illegal number: w
./icount: 19: [: Illegal number: w
./icount: 22: [: Illegal number: w
./icount: 25: [: Illegal number: w
File 'trial.txt' contains 3 lines, 19 words, and 154 characters.
Can someone help me interpret this error?

I figured it out. -eq and -ne are integer comparison operators. You have to use = and != when comparing strings.

Also you should use AND conditions in while loop
#!/bin/sh
# Prompt for filename
read -p 'Enter the file name: ' filename
# Prompt which of lines, words, or chars to display
read -p 'Count lines, words, characters, or all three (l, w, c, a)? ' display
while [ "$display" != "l" -a "$display" != "w" -a "$display" != "c" -a "$display" != "a" ]
do
echo "Invalid option"
read -p 'Count lines, words, characters, or all three (l, w, c, a)? ' display
done
# Display to stdout number of lines, words, or chars
set `wc $filename`
if [ $display == "l" ]
then
echo "File '$4' contains $1 lines."
elif [ $display == "w" ]
then
echo "File '$4' contains $2 words."
elif [ $display == "c" ]
then
echo "File '$4' contains $3 characters."
else
echo "File '$4' contains $1 lines, $2 words, and $3 characters."
fi

Related

zsh: keep slash before parenthesis

I need to cache a string from a file to do a grep search later. That string has a slash before a parenthesis. After I grab the string and echo it in the shell, it behaves the way I expect; but in my script it drops the slash off.
There's a lot of text below, but the part of the string that matters is "(remainingTime)"
Line in file:
"session_will_expire_message" = "User session will expire in \(remainingTime). Please sign in again to avoid expiration in the middle of the transaction.";
Shell Command (working as expected):
% line="\"User session will expire in \(remainingTime). Please sign in again to avoid expiration in the middle of the transaction.\";"
% echo $line
"User session will expire in \(remainingTime). Please sign in again to avoid expiration in the middle of the transaction.";
% echo -E - $line
"User session will expire in \(remainingTime). Please sign in again to avoid expiration in the middle of the transaction.";
% val="$(echo -E - $line | cut -d";" -f1 | cut -d"=" -f2-)"
% echo $val
"User session will expire in \(remainingTime). Please sign in again to avoid expiration in the middle of the transaction."
% echo -E - "${val}"
"User session will expire in \(remainingTime). Please sign in again to avoid expiration in the middle of the transaction."
Script (NOT working as expected):
while read line; do
if [[ $(echo $line | grep -c "=") -gt 0 ]]; then
key="$(echo $line | cut -d";" -f1 | cut -d"=" -f1 | tr -d " ")"
val="$(echo -E - $line | cut -d";" -f1 | cut -d"=" -f2-)"
/* !!--> */ [[ $IS_VERBOSE == "TRUE" ]] && echo "key: $key" && echo -E - "value: ${val}"
/* !!--> */ [[ $key != "" && "${val}" != "" ]] && LOCALIZED_PAIR[$key]="${val}"
fi
done < $LOCALIZED_DICT # <-- This is an URI for .strings file
The lines marked with "!! -->" are outputting to shell and to a file without the slash...
key: "session_will_expire_message"
value: "User session will expire in (remainingTime). Please sign in again to avoid expiration in the middle of the transaction."
Why is my script working differently, and how can I fix it?
EDIT:
It seems the slash is getting stripped out by the while loop:
% while read l; do echo $l; done < ~/Desktop/tmpOutput
"User session will expire in (remainingTime). Please sign in again to avoid expiration in the middle of the transaction.";
If the slashes are being split when the while loop reads the line, is there a way to avoid that?
You have a lot of unnecessary extra processes. Aside from that, though, use the -r option to prevent read from processing any backslashes in the input.
while IFS== read -r key val; do
[[ -z $key || -z $val ]] && continue
if [[ $IS_VERBOSE == TRUE ]]; then
print -r "key: $key"
print -r "value: $val"
fi
LOCALIZED_PAIR[$key]=$val
done < "$LOCALIZED_DICT"

How do I fix 'command not found' that popped out when I tried 'egrep' from a variable?

I wanted to make a program that searches all the lines that contains all the factors given, from a file mydata. I tried to egrep first factor from mydata and save it in a variable a. Then, I tried to egrep the next factor from a and save the result to a again until I egrep all the factors. But when I executed the program, it said
"command not found" in line 14.
if [ $# -eq 0 ]
then
echo -e "Usage: phoneA searchfor [...searchfor]\n(You didn't tell me what you want to search for.)"
else
a=""
for i in $*
do
if [ -z "$a" ]
then
a=$(egrep "$i" mydata)
else
a=$("$a" | egrep "$i")
fi
done
awk -f display.awk "$a"
fi
I expected all the lines including all the factors outputted on the screen in the pattern that I made in display.awk.
$a contains data, not a command. You need to write that data to the pipe.
if [ $# -eq 0 ]
then
printf '%s\n' "Usage: phoneA searchfor [...searchfor]" "(You didn't tell me what you want to search for.)" >&2
exit 1
fi
a=""
for i in "$#"; do
if [ -z "$a" ]; then
a=$(egrep "$i" mydata)
else
a=$(printf '%s' "$a" | egrep "$i")
fi
done
awk -f display.awk "$a"

Strange regular expression [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
This regular expression will match exactly one / and one . in a line. But why is it matching? Can anyone explain to me each characters role in this regular expression clearly?
if ($fp =~ m{^[^/]*/[^/]*$} and $fp =~ m{^[^.]*.[^.]$})
{
print $fp;
}
if($fp =~ m{^[^/]*/[^/]*$} and $fp =~ m{^[^.]*.[^.]$}) {
^\ / ^^\ / ^^
| | || | ||
------------- | || | ||
begin line | || | ||
--------------- || | ||
any char but / || | ||
------------------| | ||
zero or more | | ||
------------------ | ||
one / | ||
--------------------- ||
any char but / ||
------------------------|
zero or more |
------------------------|
end of line
So it search
begin or line (^),
followed by zero or more occurrence (*) of any char but / ([^/])
followed by a /
followed by zero or more occurrence (*) of any char but / ([^/])
followed by end of line ($)
The "." search is similar and the 'if' triggers if both are true.
Note that [...] searches a char in a range. For instance [abc] searches either a 'a', a 'b', or a 'c'. If first char is '^' test is reversed and [^/] is any char, but '/'.
While the previous answers are correct in explaining the regex, they do fail to point out that the 2nd regex is actually broken. As written it will match
start of line
followed by zero-or-more non-. (dot) characters
followed by ANY character, except \n
followed by ONE non-. (dot) character
end of line
Proof:
$ echo "This should NOT match" | perl -ne 'print if m{^[^.]*.[^.]$}'
This should NOT match <--- INCORRECT MATCH
$ echo "This should. match" | perl -ne 'print if m{^[^.]*.[^.]$}'
<--- INCORRECT MIS-MATCH
$ echo "This should match.!" | perl -ne 'print if m{^[^.]*.[^.]$}'
This should match.! <-- CORRECT (by luck)
$ echo "This should match." | perl -ne 'print if m{^[^.]*.[^.]$}'
This should match. <-- CORRECT
Correct would be
the . needs to be escaped (\.)
the 2nd character class needs a *
$ echo "This should NOT match" | perl -ne 'print if m{^[^.]*\.[^.]*$}'
<-- CORRECT
$ echo "This should. match" | perl -ne 'print if m{^[^.]*\.[^.]*$}'
This should. match <-- CORRECT
$ echo "This should match.!" | perl -ne 'print if m{^[^.]*\.[^.]*$}'
This should match.! <-- CORRECT
$ echo "This should match." | perl -ne 'print if m{^[^.]*\.[^.]*$}'
This should match. <-- CORRECT
The first expresion: m matches { opens expresion ^ first of line, [^/]* any character not '/' 0 or more times, '/' literal '/', again [^/]*, $ end of line, } closes the expresion.

Check whether string starts with "!" in POSIX sh

Trying to create a condition based on whether the line starts with an "!".
Note: this is sh not bash
#!/bin/sh
if [ $line = "!*" ] ;
then
echo "$0: $line has !"
else
echo "$0: $line has no !"
fi
In POSIX test, = performs exact string comparisons only.
Use a case statement instead.
case $line in
"!"*) echo "$line starts with an exclamation mark" ;;
*) echo "$line does not start with an exclamation mark" ;;
esac
If you really want to put this in an if, you can do that:
if case $string in "!"*) true;; *) false;; esac; then
echo "$line starts with an exclamation mark"
else
echo "$line does not start with an exclamation mark"
fi
You can use the standard POSIX parameter substitution syntax.
${var#repl} will remove the repl from the beginning of the content of the $var variable.
So, you'll have:
$ var=test
$ echo ${var#t}
est
$ echo ${var#X}
test
So, in order to have a simple if statement to check if a variable starts with a string (! in your case), you can have:
#!/bin/sh
if test "$line" = "${line#!}"; then
echo "$0: $line has no !"
else
echo "$0: $line has !"
fi
PS: test ... is equivalent to [ ... ], so the above script is exactly the same as
#!/bin/sh
if [ "$line" = "${line#!}" ]; then
echo "$0: $line has no !"
else
echo "$0: $line has !"
fi
Could be as simple as echo "$STR" | cut -c -1.
#!/bin/sh
STR="!abcd"
if [ "!" = $(echo "$STR" | cut -c -1) ]; then
echo "true"
fi

How to Convert While/Case statements in bash to perl

Here is the loop in bash:
while [ $# -ge 1 ]; do
case $1 in
-a)
shift
NUM_AGENTS=$1
;;
-h)
shift
HOST_NAME=$1
;;
-t)
shift
TIME_STAGGER=$1
;;
-un)
shift
USER_NAME=$1
;;
-pw)
shift
USER_PASS=$1
;;
-p)
shift
TARGET_PAGE=$1
;;
-s)
shift
COMMON_SID=$1
;;
esac
shift
done
How can i convert this in perl so that the argument would populate the values in the command line
php loadAgent_curl.php $NUM_AGENTS $HOST_NAME $procStartTime $i $TARGET_PAGE $reqlogfile $resplogfile $USER_NAME $USER_PASS $execDelay $COMMON_SID &
------- appended to question:
this certainly helps, and i really appreciate it, is there any way to access these parameters outside the getOptions ? here is rest of the bash script: my $i="0";
my $startTime=date +%s;
startTime=$[$startTime+$NUM_AGENTS+10]
my $PWD=pwd;
my $logdir="\$PWD/load-logs";
system(mkdir $logdir/$startTime);
my $reqlogfile="$logdir/$startTime/req.log";
my $resplogfile="$logdir/$startTime/resp.log";
print "\n";
print "##################\n";
print "LAUNCHING REQUESTS\n";
print " HOST NAME : \$HOST_NAME\n ";
print " TARGET PAGE : \$TARGET_PAGE\n ";
print " # AGENTS : \$NUM_AGENTS\n ";
print " EXECUTION TIME : \$startTime (with random stagger between 0 and \$TIME_STAGGER seconds)\n ";
print " REQ LOG FILE : $reqlogfile\n ";
print " RESP LOG FILE : $resplogfile\n ";
print "##################\n";
print "\n";
#
#
highestStart=$startTime
$startTime += $ARGV[0] + 5;
my $dTime = localtime( $startTime );
print "\n##################\nLAUNCHING REQUESTS\n
COUNT: $ARGV[0]\n
DELAY: | 1 \n
The scripts will fire at : $dTime\n##################\n\n";
while ( $ARGV[0] > $i )
{
$i++;
system("php avtestTimed.php $ARGV[0] $ARGV[2] $startTime");
print "RUN system('php avtestTimed.php $ARGV[0] $ARGV[2] $startTime'); \n";
sleep 1;
}
#
#
while [ $NUM_AGENTS -gt "$i" ]
do
i=$[$i+1]
execDelay=$((RANDOM % $TIME_STAGGER))"."$((RANDOM % 100))
procStartTime=$[$startTime]
procStartTime=$[$startTime+$execDelay]
if [ $procStartTime -gt $highestStart ]
then
highestStart=$procStartTime
fi
echo "STATUS: Queueing request $i with a delay of $execDelay seconds"
echo " '--> COMMAND: php loadAgent_curl.php $NUM_AGENTS $HOST_NAME $procStartTime $i $TARGET_PAGE $reqlogfile $resplogfile $USER_NAME $USER_PASS $execDelay $COMMON_SID"
php loadAgent_curl.php $NUM_AGENTS $HOST_NAME $procStartTime $i $TARGET_PAGE $reqlogfile $resplogfile $USER_NAME $USER_PASS $execDelay $COMMON_SID &
sleep 1
done
echo "STATUS: Waiting for queued requests to be ready"
while [ date +%s -lt $startTime ]
do
sleep 1
done
#
echo "STATUS: Waiting for last request to issue"
while [ date +%s -lt $highestStart ]
do
sleep 1
done
#
echo "STATUS: Last response issued"
#
echo "STATUS: Waiting for response log file to be created"
while [ ! -e "$resplogfile" ]
do
sleep 1
done
#
while [ wc -l "$resplogfile"| awk '{print $1'} -lt $NUM_AGENTS ]
do
#echo "(wc -l "$resplogfile"| awk '{print $1'} of $NUM_AGENTS responses recorded)"
sleep 1
done
echo "STATUS: FINISHED"
while true; do
read -p "Do you wish to view the request log? [y/n]" yn
case $yn in
[Yy]* ) cat $reqlogfile; break;;
[Nn]* ) exit;;
* ) echo "Please answer yes or no.";;
esac
done
while true; do
read -p "Do you wish to view the response log? [y/n]" yn
case $yn in
[Yy]* ) cat $resplogfile; break;;
[Nn]* ) exit;;
* ) echo "Please answer yes or no.";;
esac
done
Getopt::Long library is a standard Perl way to process command line options.
Something like this will work. Not tested - caveat emptor!
Please note that since your PHP parameters are a mix between command line options AND some unidentified variables, I have designed the first example so that ALL the possible options should be stored in %args hash (e.g. your program should use $args{procStartTime} instead of $procStartTime). This allowed me to make it very short and generic.
If this is hard to read/understand, I also have a second example that's more straightforward but less generic
use Getopt::Long;
my #php_arg_order = qw(a h procStartTime i p reqlogfile
resplogfile un pw execDelay s);
my %args = map {$_ => ""} #php_arg_order;
$args{procStartTime} = "something";
$args{reqlogfile} = "a.log";
# More defaults for variables NOT passed in via command line.
# Populate them all in %args as above.
# Now load actual command line parameters.
GetOptions(\%args, map { "$_=s" } #php_arg_order) or die "Unknown parameter!\n";
system(join(" ",
"php", "loadAgent_curl.php",map { $args{$_} } #php_arg_order}, "&"));
A second, less advanced but more direct option is:
use Getopt::Long;
my %args = ();
# Now load actual command line parameters.
GetOptions(\%args,
"NUM_AGENTS|a=s"
,"HOST_NAME|h=s"
,"USER_NAME|un=s"
# ... the rest of options
# The "XXX|x" notaion allows using alias "-x" parameter
# but stores in $args{XXX} instead for better readability
) or die "Unknown parameter!\n";
system("php loadAgent_curl.php $args{NUM_AGENTS} $args{HOST_NAME} $procStartTime $i $args{TARGET_PAGE} $reqlogfile $resplogfile $args{USER_NAME} $args{USER_PASS} $execDelay $args{COMMON_SID} &");