Linux Shell Script Illegal number - linux

I am trying to find out the number of occurrences of pattern in a file with following code:
#!/bin/sh
var='grep -c 'abc' file1'
if [ "$var" -lt 10 ]; then
echo "less than 10"
fi
I am getting the error: Illegal number: grep -c abc file1
Can someone please help.
Thanks.

Use backticks (`) instead of apostrophes ('):
#!/bin/sh
var=`grep -c 'abc' file1`
if [ "$var" -lt 10 ]; then
echo "less than 10"
fi

You probably want backticks (`) rather than single-quotes ('). i.e.:
var=`grep -c 'abc' file1`

You've used a single quote rather than a backtick so your var variable is actually set to a string literal rather than the result of that command. You'd see that if you echoed the variable first:
pax$ var='grep -c 'abc' file1'
pax$ echo "[$var]"
[grep -c abc file1]
The backtick version would be:
var=`grep -c 'abc' file1`
But I'd like to suggest using bash where possible for scripting. You'll be hard-pressed finding a mainstream distro that doesn't have it by default and it's considered by some to be more powerful than other shells. In fact, on some systems, /bin/sh is bash.
If you can go that rute, the $() construct is usually a better idea since you can nest them without pain:
var=$(grep -c 'abc' file1)

Try posting the followings to get better answer:
grep --version
bash --version if your shell is Bash or let us know which shell you are using.
use grep within back-ticks or as shown below
[[ is more versatile in Bash than [.
Finally, the following works on my machine without any error:
#!/bin/bash
var=$(grep -c "abc" file1)
if [[ "$var" -lt 10 ]]
then
echo "less than 10"
fi
Execution:
user#machine:~$ cat file1
abc
abcd
abcde
abcdef
user#machine:~$
user#machine:~$ ./t.sh
less than 10
user#machine:~$

Related

What do three left angle brackets (`<<<`) mean in bash? [duplicate]

I'm getting this error
Syntax error: redirection unexpected
in the line:
if grep -q "^127.0.0." <<< "$RESULT"
How I can run this in Ubuntu?
<<< is a bash-specific redirection operator (so it's not specific to Ubuntu). The documentation refers to it as a "Here String", a variant of the "Here Document".
3.6.7 Here Strings
A variant of here documents, the format is:
<<< word
The word is expanded and supplied to the command on its
standard input.
A simple example:
$ cat <<< hello
hello
If you're getting an error, it's likely that you're executing the command using a shell other than bash. If you have #!/bin/sh at the top of your script, try changing it to #!/bin/bash.
If you try to use it with /bin/sh, it probably assumes the << refers to a "here document", and then sees an unexpected < after that, resulting in the "Syntax error: redirection unexpected" message that you're seeing.
zsh and ksh also support the <<< syntax.
if grep -q "^127.0.0." <<< "$RESULT"
then
echo IF-THEN
fi
is a Bash-specific thing. If you are using a different bourne-compatable shell, try:
if echo "$RESULT" | grep -q "^127.0.0."
then
echo IF-THEN
fi
It works for me on Ubuntu, if I complete you IF block:
if grep -q "^127.0.0." <<< "$RESULT"; then echo ""; fi

Evaluation of curly braces in Linux

I’ve noticed that we can use curly braces to make some of the commands much shorter as it is evaluated into list of arguments.
Input:
echo a{,b,c}
Output:
a ab ac
How do I force the same behaviour when the arguments are passed from the file?
Input:
cat file.txt | xargs echo
Output:
a{,b,c}
Expected output - same as in the previous example.
That {} expansion is a bash / zsh feature, as such then you need to explicitly run it thru any of these shells, in your case would be (using -I<STRING> to let xargs replace it in the string before running it):
cat file.txt |xargs -I# bash -c 'echo #'
xargs calls the echo as found in the $PATH, not the shell's builtin echo.
check the list of bash expansions: brace expansion happens first, so it won't get a chance to expand in that pipeline.
You'll have to do something like
while read -r line; do eval echo "$line"; done < file.txt
which exposes you to all kinds of nasty attacks if someone puts something malicious in that file.
Other than asking why would you want to do this... I offer the following:
add the string to a file:
echo 'a{,b,c}' > /tmp/foo
put the string in a variable:
export thing=`cat /tmp/foo`
eval the string:
eval $thing
If you had a bunch of these in a file then run the file through a loop and eval the loop value:
echo 'a{,b,c}' >> /tmp/foo
echo 'a{,b,c}' >> /tmp/foo
echo 'a{,b,c}' >> /tmp/foo
for i in `cat /tmp/foo`; do eval echo $i; done

Searching a string in shell script

I am trying to learn shell script. So sorry if my question is so simple.
I am having a file called one.txt and if either strings 1.2 or 1.3 is present in the string then I have to display the success message else the failure message.
The code I tried is follows,
#!/bin/bash
echo "checking"
if grep -q 1.2 /root/one | grep -q 1.3 /root/one; then
echo " vetri Your NAC version"
fi
What I am doing wrong here ?
You can also include the OR in your grep pattern like so:
grep '1.2\|1.3' /root/one
details here
Update:
as twalberg pointed out in the comment, my answer was not precise enough. The better pattern is:
grep '1\.2\|1\.3' /root/one
Or even better, because more compact:
grep '1\.[23]' /root/one
You have to use ||
#!/bin/bash
echo "checking"
if grep -q 1.2 /root/one || grep -q 1.3 /root/one; then
echo " vetri Your NAC version"
fi
Single | operator is called pipe. It will pass the output of the command before | to the command after |.
It is better to join these these greps with | (OR operator):
grep '1.2\|1.3'
or
grep -E '1.2|1.3'
I guess the easier way to do this is to create a variable to check the count of occurrences:
#!/bin/bash
echo "checking"
CHECK=`egrep -c '1\.(2|3)' /root/one`
if [ "$CHECK" -gt 0 ]; then
echo "vetri Your NAC version"
fi

How do I echo "-e"?

I want to echo a string that might contain the same parameters as echo. How can I do it without modifying the string?
For instance:
$ var="-e something"
$ echo $var
something
... didn't print -e
A surprisingly deep question. Since you tagged bash, I'll assume you mean bash's internal echo command, though the GNU coreutils' standalone echo command probably works similarly enough.
The gist of it is: if you really need to use echo (which would be surprising, but that's the way the question is written by now), it all depends on what exactly your string can contain.
The easy case: -e plus non-empty string
In that case, all you need to do is quote the variable before passing it to echo.
$ var="-e something"
$ echo "$var"
-e something
If the string isn't eaxctly an echo option or combination, which includes any non-option suffix, it won't be recognized as such by echo and will be printed out.
Harder: string can be -e only
If your case can reduce to just "-e", it gets trickier. One way to do it would be:
$ echo -e '\055e'
-e
(escaping the dash so it doesn't get interpreted as an option but as on octal sequence)
That's rewriting the string. It can be done automatically and non-destructively, so it feels acceptable:
$ var="-e something"
$ echo -e ${var/#-/\\055}
-e something
You noticed I'm actually using the -e option to interpret an octal sequence, so it won't work if you intended to echo -E. It will work for other options, though.
The right way
Seriously, you're not restricted to echo, are you?
printf '%s\n' "$var"
The proper bash way is to use printf:
printf "%s\n" "$var"
By the way, your echo didn't work because when you run:
var="-e something"
echo $var
(without quoting $var), echo will see two arguments: -e and something. Because when echo meets -e as its first argument, it considers it's an option (this is also true for -n and -E), and so processes it as such. If you had quoted var, as shown in other answers, it would have worked.
Quote it:
$ var="-e something"
$ echo "$var"
-e something
If what you want is to get echo -e's behaviour (enable interpretation of backslash escapes), then you have to leave the $var reference without quotes:
$ var="hi\nho"
$ echo $var
hi
ho
Or use eval:
$ var="hi\nho"
$ eval echo \${var}
hi\nho
$ var="-e hi\nho"
$ eval echo \${var}
hi
ho
Since we're using bash, another alternative to echo is to simply cat a "here string":
$ var="-e something"
$ cat <<< "$var"
-e something
$ var="-e"
$ cat <<< "$var"
-e
$
printf-based solutions will almost certainly be more portable though.
Try the following:
$ env POSIXLY_CORRECT=1 echo -e
-e
Due to shell aliases and built-in echo command, using an unadorned
echo interactively or in a script may get you different functionality
than that described here. Invoke it via env (i.e., env echo ...)
to avoid interference from the shell.
The environment variable POSIXLY_CORRECT was introduced to allow the user to force the standards-compliant behaviour. See: POSIX at Wikipedia.
Or use printf:
$ printf '%s\n' "$var"
Source: Why is bash swallowing -e in the front of an array at stackoverflow SE
Use printf instead:
var="-e bla"
printf "%s\n" "$var"
Using just echo "$var" will still fail if var contains just a -e or similar. If you need to be able to print that as well, use printf.

Forcing bash to expand variables in a string loaded from a file

I am trying to work out how to make bash (force?) expand variables in a string (which was loaded from a file).
I have a file called "something.txt" with the contents:
hello $FOO world
I then run
export FOO=42
echo $(cat something.txt)
this returns:
hello $FOO world
It didn't expand $FOO even though the variable was set. I can't eval or source the file - as it will try and execute it (it isn't executable as it is - I just want the string with the variables interpolated).
Any ideas?
I stumbled on what I think is THE answer to this question: the envsubst command:
echo "hello \$FOO world" > source.txt
export FOO=42
envsubst < source.txt
This outputs: hello 42 world
If you would like to continue work on the data in a file destination.txt, push this back to a file like this:
envsubst < source.txt > destination.txt
In case it's not already available in your distro, it's in the
GNU package gettext.
#Rockallite
I wrote a little wrapper script to take care of the '$' problem.
(BTW, there is a "feature" of envsubst, explained at
https://unix.stackexchange.com/a/294400/7088
for expanding only some of the variables in the input, but I
agree that escaping the exceptions is much more convenient.)
Here's my script:
#! /bin/bash
## -*-Shell-Script-*-
CmdName=${0##*/}
Usage="usage: $CmdName runs envsubst, but allows '\$' to keep variables from
being expanded.
With option -sl '\$' keeps the back-slash.
Default is to replace '\$' with '$'
"
if [[ $1 = -h ]] ;then echo -e >&2 "$Usage" ; exit 1 ;fi
if [[ $1 = -sl ]] ;then sl='\' ; shift ;fi
sed 's/\\\$/\${EnVsUbDolR}/g' | EnVsUbDolR=$sl\$ envsubst "$#"
Many of the answers using eval and echo kind of work, but break on various things, such as multiple lines, attempting to escaping shell meta-characters, escapes inside the template not intended to be expanded by bash, etc.
I had the same issue, and wrote this shell function, which as far as I can tell, handles everything correctly. This will still strip only trailing newlines from the template, because of bash's command substitution rules, but I've never found that to be an issue as long as everything else remains intact.
apply_shell_expansion() {
declare file="$1"
declare data=$(< "$file")
declare delimiter="__apply_shell_expansion_delimiter__"
declare command="cat <<$delimiter"$'\n'"$data"$'\n'"$delimiter"
eval "$command"
}
For example, you can use it like this with a parameters.cfg which is really a shell script that just sets variables, and a template.txt which is a template that uses those variables:
. parameters.cfg
printf "%s\n" "$(apply_shell_expansion template.txt)" > result.txt
In practice, I use this as a sort of lightweight template system.
you can try
echo $(eval echo $(cat something.txt))
You don't want to print each line, you want to evaluate it so that Bash can perform variable substitutions.
FOO=42
while read; do
eval echo "$REPLY"
done < something.txt
See help eval or the Bash manual for more information.
Another approach (which seems icky, but I am putting it here anyway):
Write the contents of something.txt to a temp file, with an echo statement wrapped around it:
something=$(cat something.txt)
echo "echo \"" > temp.out
echo "$something" >> temp.out
echo "\"" >> temp.out
then source it back in to a variable:
RESULT=$(source temp.out)
and the $RESULT will have it all expanded. But it seems so wrong !
Single line solution that doesn't need temporary file :
RESULT=$(source <(echo "echo \"$(cat something.txt)\""))
#or
RESULT=$(source <(echo "echo \"$(<something.txt)\""))
If you only want the variable references to be expanded (an objective that I had for myself) you could do the below.
contents="$(cat something.txt)"
echo $(eval echo \"$contents\")
(The escaped quotes around $contents is key here)
If something.txt has only one line, a bash method, (a shorter version of Michael Neale's "icky" answer),
using process & command substitution:
FOO=42 . <(echo -e echo $(<something.txt))
Output:
hello 42 world
Note that export isn't needed.
If something.txt has one or more lines, a GNU sed evaluate method:
FOO=42 sed 's/"/\\\"/g;s/.*/echo "&"/e' something.txt
Following solution:
allows replacing of variables which are defined
leaves unchanged variables placeholders which are not defined. This is especially useful during automated deployments.
supports replacement of variables in following formats:
${var_NAME}
$var_NAME
reports which variables are not defined in environment and returns error code for such cases
TARGET_FILE=someFile.txt;
ERR_CNT=0;
for VARNAME in $(grep -P -o -e '\$[\{]?(\w+)*[\}]?' ${TARGET_FILE} | sort -u); do
VAR_VALUE=${!VARNAME};
VARNAME2=$(echo $VARNAME| sed -e 's|^\${||g' -e 's|}$||g' -e 's|^\$||g' );
VAR_VALUE2=${!VARNAME2};
if [ "xxx" = "xxx$VAR_VALUE2" ]; then
echo "$VARNAME is undefined ";
ERR_CNT=$((ERR_CNT+1));
else
echo "replacing $VARNAME with $VAR_VALUE2" ;
sed -i "s|$VARNAME|$VAR_VALUE2|g" ${TARGET_FILE};
fi
done
if [ ${ERR_CNT} -gt 0 ]; then
echo "Found $ERR_CNT undefined environment variables";
exit 1
fi
foo=45
file=something.txt # in a file is written: Hello $foo world!
eval echo $(cat $file)
$ eval echo $(cat something.txt)
hello 42 world
$ bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin17)
Copyright (C) 2007 Free Software Foundation, Inc.
envsubst is a great solution (see LenW's answer) if the content you're substituting is of "reasonable" length.
In my case, I needed to substitute in a file's content to replace the variable name. envsubst requires that the content be exported as environment variables and bash has a problem when exporting environment variables that are more than a megabyte or so.
awk solution
Using cuonglm's solution from a different question:
needle="doc1_base64" # The "variable name" in the file. (A $ is not needed.)
needle_file="doc1_base64.txt" # Will be substituted for the needle
haystack=$requestfile1 # File containing the needle
out=$requestfile2
awk "BEGIN{getline l < \"${needle_file}\"}/${needle}/{gsub(\"${needle}\",l)}1" $haystack > $out
This solution works for even large files.
expenv () {
LF=$'\n'
echo "cat <<END_OF_TEXT${LF}$(< "$1")${LF}END_OF_TEXT" | bash
return $?
}
expenv "file name"
The following works: bash -c "echo \"$(cat something.txt)"\"

Resources