4

This question already has an answer here:

I have a variable which stores a string, the output of a sed command. I want to execute a set of commands only if this string value matches either of the 2 other strings.

I used the below code.

#! /bin/ksh
request=”Request”
fault=”Fault”
while read lines; do
    category=`echo $lines|sed -n -e 's/.*Summary: Value//p'| awk '{print $1}'`
    if [ ! -z "$category" ]
    then
        if($category = $request)
        then
            echo $category
        fi
     fi
done<content.txt

But it's giving me an error:

sample.sh: Request: not found

The variable category will have either value Request or value Order

Can someone point the error or a solution to this?

If inner if is eliminated and echo $category will print the exact string value.

marked as duplicate by Gilles shell-script Aug 31 '18 at 7:34

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • 5
    You'll find it much easier to read code if you learn to indent it to represent the block structure. – roaima Aug 30 '18 at 14:00
12

Simple answer to do so you can use this syntax :

if [ "$category" = "$request" ]

Spacing is important. Side note, you should use the recent way of doing command subsitition and replace (see article 1 and posix article):

category=`echo $lines|sed -n -e 's/.*Summary: value//p'| awk '{print $1}'`

by this

category=$(echo $lines|sed -n -e 's/.*Summary: value//p'| awk '{print $1}')
10

Other's have already pointed out the syntax error in your if statement.

I'd also like to comment on the fact that you seem to use typographical double quotes, , instead of ordinary ones, ". You also don't need to store the output of sed in a separate variable through that command substitution. Instead, read from the awk output directly:

#!/bin/ksh

sed -n 's/.*Summary: Value//p' <content.txt | awk '{ print $1 }' |
while IFS= read -r category; do
    case $category in
        Request|Fault)
            printf '%s\n' "$category"
            ;;
        Breakfast)
            echo 'Yum'
            ;;
        *)
            printf 'Unknown category: %s\n' "$category"
    esac
done

Or, if you're more comfortable with if-statements,

#!/bin/ksh

sed -n 's/.*Summary: Value//p' <content.txt | awk '{ print $1 }' |
while IFS= read -r category; do
    if   [ "$category" = 'Request' ] || [ "$category" = 'Fault' ]; then
        printf '%s\n' "$category"
    elif [ "$category" = 'Breakfast' ]; then
        echo 'Yum'
    else
        printf 'Unknown category: %s\n' "$category"
    fi
done

Note that there is no need to test whether the string is empty. An empty string may be compared to another string without problems, as long as you always quote the variable expansion.

Or, if your script is doing nothing else than this:

#!/bin/ksh

sed -n 's/.*Summary: Value//p' <content.txt | awk '
    $1 == "Request" || $1 == "Fault" { print $1;    next }
    $1 == "Breakfast"                { print "Yum"; next }
                                     { printf("Unknown category: %s\n", $1) }

Related:

  • I have to search some other strings also in the text file to execute certain commands. Thats why I used a loop. – Devjith Aug 30 '18 at 9:59
  • @Devjith See updated answer. – Kusalananda Aug 30 '18 at 10:10
  • How can I modify the if condition so that i will execute the commands inside if condition when category is either request or fault? For either of the categories same commands are to be executed. In java, we could use "OR" inside if. So similarly, how can it be written here? – Devjith Aug 30 '18 at 10:28
  • @Devjith If you have a second look at the code in the answer, you will notice that this is exactly what I do for the cases when $category is either Request or Fault. – Kusalananda Aug 30 '18 at 10:29
  • 1
    @Devjith Please update your question with enough information for us to be able to help you. – Kusalananda Aug 30 '18 at 10:33
5

We should use [ instead of ( in if the condition

Try:

 if [ "$category" = "$request" ]
  • () used to execute the command and check the exit code.
  • [] used to check the expression.

And I hope you have copied the script from MS-word since it has non-unix character

request=”Request”
fault=”Fault”

Edit this as

request="Request"
fault="Fault"
  • 1
    What is a „non-unix character“? If the locale is set to a suitable encoding/character set, e.g. UTF-8, then typographical quotes are usable characters. – BlackJack Aug 30 '18 at 13:29
  • 1
    @BlackJack indeed and are usable characters, but for the purposes of shell syntax.they are neither seen nor treated as single ' or double " quotes. – roaima Aug 30 '18 at 14:01
3

I want to just comment on the most important part:

if($category = $request)

A shell if works another way, which is by running a command and using its return code.

An example is:

if /usr/bin/somecommand; then
    echo "somecommand exited with exit code 0 (success)"
fi

to compare two strings, you would use

if /bin/test a = b; then
    echo "a=b"
fi

Note that test may be a builtin in your shell, but you usually have it as binary as well.

The next thing is, that you usually have a symlink from /bin/[ to /bin/test. This means you can do:

if [ a = b ]; then
    echo "a=b"
fi

where [ a = b ] is the same as test a = b and the trailing ] is just there for a nicer syntax.
This is the reason, why if [a=b] won't work, as the syntax above means [ "a" "b" "]", where [ is a program name. Without the space, the shell is looking for [a=b].

Your syntax using (a = b) uses a subshell to run the command inside, resulting in a command a = b, where a is considered to be a program name.

Another possible pitfall of your code is, that variables may be empty. Have a look at this code:

b=something
if [ $a = $b ]; then
    echo $a = $b
fi

This will give an error, because it is equivalent to test = something (running test "=" "something"). The echo below has a similar problem, but for echo this does not matter very much. The way to fix this is using appropriate quotes:

b=something
if [ "$a" = "$b" ]; then
    echo "$a = $b"
fi

Resulting in the test command line: test "" "=" "something", which is a correct command. I fixed potential problems on the echo line by putting the whole string into quotes, which make them one single parameter for the echo command (or builtin).

  • Rather than "Found some Textfiles" (note the closing quote BTW), that particular test should print "ls ran ok". To check whether there are any filenames matching *.txt, use set -- *.txt; if [ "$#" -gt 0 ]; then .... – Kusalananda Aug 30 '18 at 12:53
  • I changed it a bit. Let's not make it too difficult, because the point there is to show some command which can be used instead of test. If you know some more obvious example feel free to edit it. But please mind that this should explain how the syntax is working and not find the easiest/most efficient/fastest/smartest solution to the problem. – allo Aug 30 '18 at 13:11
  • The semicolons at the end of the if lines are unnecessary. They are just needed if the then is on the same line. – BlackJack Aug 30 '18 at 13:30
  • Yes and no. They are there to make a point, in fact they just are needed / unneeded as any other semicolons. The semicolon on the "ls ran fine" line is inconsistent, though. I reformat again to make it more consistent, as I wanted to explain as clear as possible. – allo Aug 30 '18 at 13:33
1

In addition to what all the other answers said,

  1. awk is a very powerful program.  It’s rare that you need to couple it with another text-formatting command.  For example,
    sed -n -e 's/.*Summary: Value//p' | awk '{print $1}'
    can be converted to
    awk '{ if (sub(/.*Summary: Value/, "")) print $1 }'
  2. You should always quote shell variables (e.g., "$lines", "$category" and "$request") unless you have a good reason not to, and you’re sure you know what you’re doing.
  3. As Kusalananda pointed out, using a shell loop to process text is considered bad practice.  As I said above, awk is a very powerful program.  I presume that you understand that your script reads your file one line at a time, and invokes awk (and sed!) once for each line.  Without explicitly pointing it out, Kusalananda gave you example solutions which run awk (and sed) once for the entire file; the last one puts all the logic into the awk script, rather than shell code.  That is probably the best approach if all you’re doing is printing strings, as your question indicates.  But, even if you need to run other commands based on the input, you can do this using the system() function in awk.  The complexity of your task will be a factor in deciding which is the better approach.
0

Here's a bit more creative way. If I understood correctly, you only want to act if $category is Order or Request (or any two things) and act the same.

if (( $(echo -e "Request\nOrder\n$category" | sort -u | wc -l) == 2)); then
  echo $category
fi

It's just using the fact that if the desired word is used, sort will remove one line due to making the list unique (-u). Otherwise the list will remain in three lines.

0

This should do work as there are () instead of [] on your statment if($category = $request)

#!/bin/ksh
request="Request"
fault="Fault"
while read lines; do
    category=$(echo $lines|sed -n -e 's/.*Summary: Value//p'| awk '{print $1}')
    if [ ! -z "$category" ]
    then
        if [[ $category = $request ]]
        then
            echo $category
        fi
     fi
    done<input.txt

    #contents of input.txt
    Summary: Value Request
    Summary: Value Order

Not the answer you're looking for? Browse other questions tagged or ask your own question.