Redirector "<<<" in Ubuntu? - bash

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

Related

Error while running a while loop in Unix

I am executing a script and getting error in the following code. It is working fine on RedHat but in AIX7.1 it is giving errors :
while read line
do
read -A arr <<< $line
ct="$(perl -e 'print time()')"
x=${arr[6]}
y="$(((ct-x)/60/60))"
if [ $y -gt 48 ];then
echo "${arr[0]} ${arr[3]} ${arr[5]} ${arr[6]}" >> $longrunning_jobs_tmp1
fi
done < $active_jobs_tmp4
I have correctly defined the variables also but still getting the following issue :
Job_Monitoring_Test.ksh[121]: 0403-057 Syntax error at line 123 : `<' is not expected.
You are running the script with ksh88 on AIX while it was written for ksh93.
Try setting its shebang to:
#!/bin/ksh93
...
If ksh93 is not available, perhaps dtksh would work. Otherwise, you need to port the script to bash.
Edit:
If the issue is with the <<< syntax, you can replace
read -A arr <<< $line
by:
read -A arr <<%EOF%
$line
%EOF%
Do you have/can you install Bash on your system?
Using the shebang #/bin/bash might solve a lot problems.
You still have to look at the options used in different commands like find (mtime), date and sed (-i option for inplace editing), but Bash specific constructions will work.

Capturing verbatim command line (including quotes!) to call inside script

I'm trying to write a "phone home" script, which will log the exact command line (including any single or double quotes used) into a MySQL database. As a backend, I have a cgi script which wraps the database. The scripts themselves call curl on the cgi script and include as parameters various arguments, including the verbatim command line.
Obviously I have quite a variety of quote escaping to do here and I'm already stuck at the bash stage. At the moment, I can't even get bash to print verbatim the arguments provided:
Desired output:
$ ./caller.sh -f -hello -q "blah"
-f hello -q "blah"
Using echo:
caller.sh:
echo "$#"
gives:
$ ./caller.sh -f -hello -q "blah"
-f hello -q blah
(I also tried echo $# and echo $*)
Using printf %q:
caller.sh:
printf %q $#
printf "\n"
gives:
$ ./caller.sh -f hello -q "blah"
-fhello-qblah
(I also tried print %q "$#")
I would welcome not only help to fix my bash problem, but any more general advice on implementing this "phone home" in a tidier way!
There is no possible way you can write caller.sh to distinguish between these two commands invoked on the shell:
./caller.sh -f -hello -q "blah"
./caller.sh -f -hello -q blah
There are exactly equivalent.
If you want to make sure the command receives special characters, surround the argument with single quotes:
./caller.sh -f -hello -q '"blah"'
Or if you want to pass just one argument to caller.sh:
./caller.sh '-f -hello -q "blah"'
You can get this info from the shell history:
function myhack {
line=$(history 1)
line=${line#* }
echo "You wrote: $line"
}
alias myhack='myhack #'
Which works as you describe:
$ myhack --args="stuff" * {1..10} $PATH
You wrote: myhack --args="stuff" * {1..10} $PATH
However, quoting is just the user's way of telling the shell how to construct the program's argument array. Asking to log how the user quotes their arguments is like asking to log how hard the user punched the keys and what they were wearing at the time.
To log a shell command line which unambiguously captures all of the arguments provided, you don't need any interactive shell hacks:
#!/bin/bash
line=$(printf "%q " "$#")
echo "What you wrote would have been indistinguishable from: $line"
I understand you want to capture the arguments given by the caller.
Firstly, quotes used by the caller are used to protect during the interpretation of the call. But they do not exist as argument.
An example: If someone call your script with one argument "Hello World!" with two spaces between Hello and World. Then you have to protect ALWAYS $1 in your script to not loose this information.
If you want to log all arguments correctly escaped (in the case where they contains, for example, consecutive spaces...) you HAVE to use "$#" with double quotes. "$#" is equivalent to "$1" "$2" "$3" "$4" etc.
So, to log arguments, I suggest the following at the start of the caller:
i=0
for arg in "$#"; do
echo "arg$i=$arg"
let ++i
done
## Example of calls to the previous script
#caller.sh '1' "2" 3 "4 4" "5 5"
#arg1=1
#arg2=2
#arg3=3
#arg4=4 4
#arg5=5 5
#Flimm is correct, there is no way to distinguish between arguments "foo" and foo, simply because the quotes are removed by the shell before the program receives them. What you need is "$#" (with the quotes).

Why do I get a “Can't find string terminator” error only when the command is in a variable?

I wrote a simple shell script to get the version of Perl modules installed
on a server and I keep receiving the following error:
Can't find string terminator "'" anywhere before EOF at -e line 1.
Here is my script:
#!/bin/sh
#
mod_name="Sub::Uplevel"
tmp1="perl -M$mod_name -e 'print \"\$$mod_name::VERSION\"'"
echo $tmp1
$tmp1
If I just directly run the echo'd line (perl -MSub::Uplevel -e 'print "$Sub::Uplevel::VERSION"'), it works. Why doesn't the line work when its run from the variable $tmp1?
In place of just $tmp1, eval works:
eval "$tmp1"
That's because splitting a variable into words (for arguments) is done strictly by splitting on $IFS, not the normal input-parsing. eval forces the normal input parsing.
How did I figure this out?
Change your tmp1= line to put an echo in front, and you get:
perl -MSub::Uplevel -e 'print "$Sub::Uplevel::VERSION"'
Note that the ' are still there, which you wouldn't expect. If you write a quick script:
#!/bin/sh
for a in "$#"; do
echo "arg: $a"
done
and put a call to that in place of echo, you find how the arguments are really split:
arg: perl
arg: -MSub::Uplevel
arg: -e
arg: 'print
arg: "$Sub::Uplevel::VERSION"'
So, you can see that's splitting on spaces, so IFS.
It's always better to construct commands using bash arrays. That will keep arguments with whitespace properly grouped:
#!/bin/bash
mod_name="Sub::Uplevel"
perl_script=$(printf 'print "$%s::VERSION"' $mod_name)
tmp1=(perl -M$mod_name -e "$perl_script")
echo "${tmp1[#]}"
output=$( "${tmp1[#]}" )
Arrays are a bash feature, so the shebang line must reference bash not sh.
I'd usually write what you are doing with backticks, to run the command inside the shell:
#!/bin/sh
#
mod_name="Sub::Uplevel"
tmp1=`perl -M$mod_name -e 'print \"\$$mod_name::VERSION\"'`
echo $tmp1
Then you can work on $tmp1 as needed. It also avoids dealing with escaping.
Try to execute the script the below way(debugging the script):
sh -vx your_script.sh
Then you would be able to see where exactly the problem is.
I donot have the shell to execute it right now.

Echo newline in Bash prints literal \n

How do I print a newline? This merely prints \n:
$ echo -e "Hello,\nWorld!"
Hello,\nWorld!
Use printf instead:
printf "hello\nworld\n"
printf behaves more consistently across different environments than echo.
Make sure you are in Bash.
$ echo $0
bash
All these four ways work for me:
echo -e "Hello\nworld"
echo -e 'Hello\nworld'
echo Hello$'\n'world
echo Hello ; echo world
echo $'hello\nworld'
prints
hello
world
$'' strings use ANSI C Quoting:
Words of the form $'string' are treated specially. The word expands to string, with backslash-escaped characters replaced as specified by the ANSI C standard.
You could always do echo "".
For example,
echo "Hello,"
echo ""
echo "World!"
On the off chance that someone finds themselves beating their head against the wall trying to figure out why a coworker's script won't print newlines, look out for this:
#!/bin/bash
function GET_RECORDS()
{
echo -e "starting\n the process";
}
echo $(GET_RECORDS);
As in the above, the actual running of the method may itself be wrapped in an echo which supersedes any echos that may be in the method itself. Obviously, I watered this down for brevity. It was not so easy to spot!
You can then inform your comrades that a better way to execute functions would be like so:
#!/bin/bash
function GET_RECORDS()
{
echo -e "starting\n the process";
}
GET_RECORDS;
Simply type
echo
to get a new line
POSIX 7 on echo
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html
-e is not defined and backslashes are implementation defined:
If the first operand is -n, or if any of the operands contain a <backslash> character, the results are implementation-defined.
unless you have an optional XSI extension.
So I recommend that you should use printf instead, which is well specified:
format operand shall be used as the format string described in XBD File Format Notation [...]
the File Format Notation:
\n <newline> Move the printing position to the start of the next line.
Also keep in mind that Ubuntu 15.10 and most distros implement echo both as:
a Bash built-in: help echo
a standalone executable: which echo
which can lead to some confusion.
str='hello\nworld'
$ echo | sed "i$str"
hello
world
You can also do:
echo "hello
world"
This works both inside a script and from the command line.
On the command line, press Shift+Enter to do the line break inside the string.
This works for me on my macOS and my Ubuntu 18.04 (Bionic Beaver) system.
For only the question asked (not special characters etc) changing only double quotes to single quotes.
echo -e 'Hello,\nWorld!'
Results in:
Hello,
World!
There is a new parameter expansion added in Bash 4.4 that interprets escape sequences:
${parameter#operator} - E operator
The expansion is a string that is the value of parameter with
backslash escape sequences expanded as with the $'…' quoting
mechanism.
$ foo='hello\nworld'
$ echo "${foo#E}"
hello
world
I just use echo without any arguments:
echo "Hello"
echo
echo "World"
To print a new line with echo, use:
echo
or
echo -e '\n'
This could better be done as
x="\n"
echo -ne $x
-e option will interpret backslahes for the escape sequence
-n option will remove the trailing newline in the output
PS: the command echo has an effect of always including a trailing newline in the output so -n is required to turn that thing off (and make it less confusing)
My script:
echo "WARNINGS: $warningsFound WARNINGS FOUND:\n$warningStrings
Output:
WARNING : 2 WARNINGS FOUND:\nWarning, found the following local orphaned signature file:
On my Bash script I was getting mad as you until I've just tried:
echo "WARNING : $warningsFound WARNINGS FOUND:
$warningStrings"
Just hit Enter where you want to insert that jump. The output now is:
WARNING : 2 WARNINGS FOUND:
Warning, found the following local orphaned signature file:
If you're writing scripts and will be echoing newlines as part of other messages several times, a nice cross-platform solution is to put a literal newline in a variable like so:
newline='
'
echo "first line${newline}second line"
echo "Error: example error message n${newline}${usage}" >&2 #requires usage to be defined
If the previous answers don't work, and there is a need to get a return value from their function:
function foo()
{
local v="Dimi";
local s="";
.....
s+="Some message here $v $1\n"
.....
echo $s
}
r=$(foo "my message");
echo -e $r;
Only this trick worked on a Linux system I was working on with this Bash version:
GNU bash, version 2.2.25(1)-release (x86_64-redhat-linux-gnu)
You could also use echo with braces,
$ (echo hello; echo world)
hello
world
This got me there....
outstuff=RESOURCE_GROUP=[$RESOURCE_GROUP]\\nAKS_CLUSTER_NAME=[$AKS_CLUSTER_NAME]\\nREGION_NAME=[$REGION_NAME]\\nVERSION=[$VERSION]\\nSUBNET-ID=[$SUBNET_ID]
printf $outstuff
Yields:
RESOURCE_GROUP=[akswork-rg]
AKS_CLUSTER_NAME=[aksworkshop-804]
REGION_NAME=[eastus]
VERSION=[1.16.7]
SUBNET-ID=[/subscriptions/{subidhere}/resourceGroups/makeakswork-rg/providers/Microsoft.Network/virtualNetworks/aks-vnet/subnets/aks-subnet]
Sometimes you can pass multiple strings separated by a space and it will be interpreted as \n.
For example when using a shell script for multi-line notifcations:
#!/bin/bash
notify-send 'notification success' 'another line' 'time now '`date +"%s"`
With jq:
$ jq -nr '"Hello,\nWorld"'
Hello,
World
Additional solution:
In cases, you have to echo a multiline of the long contents (such as code/ configurations)
For example:
A Bash script to generate codes/ configurations
echo -e,
printf might have some limitation
You can use some special char as a placeholder as a line break (such as ~) and replace it after the file was created using tr:
echo ${content} | tr '~' '\n' > $targetFile
It needs to invoke another program (tr) which should be fine, IMO.

How to use the read command in Bash?

When I try to use the read command in Bash like this:
echo hello | read str
echo $str
Nothing echoed, while I think str should contain the string hello. Can anybody please help me understand this behavior?
The read in your script command is fine. However, you execute it in the pipeline, which means it is in a subshell, therefore, the variables it reads to are not visible in the parent shell. You can either
move the rest of the script in the subshell, too:
echo hello | { read str
echo $str
}
or use command substitution to get the value of the variable out of the subshell
str=$(echo hello)
echo $str
or a slightly more complicated example (Grabbing the 2nd element of ls)
str=$(ls | { read a; read a; echo $a; })
echo $str
Other bash alternatives that do not involve a subshell:
read str <<END # here-doc
hello
END
read str <<< "hello" # here-string
read str < <(echo hello) # process substitution
Typical usage might look like:
i=0
echo -e "hello1\nhello2\nhello3" | while read str ; do
echo "$((++i)): $str"
done
and output
1: hello1
2: hello2
3: hello3
The value disappears since the read command is run in a separate subshell: Bash FAQ 24
To put my two cents here: on KSH, reading as is to a variable will work, because according to the IBM AIX documentation, KSH's read does affects the current shell environment:
The setting of shell variables by the read command affects the current shell execution environment.
This just resulted in me spending a good few minutes figuring out why a one-liner ending with read that I've used a zillion times before on AIX didn't work on Linux... it's because KSH does saves to the current environment and BASH doesn't!
I really only use read with "while" and a do loop:
echo "This is NOT a test." | while read -r a b c theRest; do
echo "$a" "$b" "$theRest"; done
This is a test.
For what it's worth, I have seen the recommendation to always use -r with the read command in bash.
You don't need echo to use read
read -p "Guess a Number" NUMBER
Another alternative altogether is to use the printf function.
printf -v str 'hello'
Moreover, this construct, combined with the use of single quotes where appropriate, helps to avoid the multi-escape problems of subshells and other forms of interpolative quoting.
Do you need the pipe?
echo -ne "$MENU"
read NUMBER

Resources