shell : syntax error near unexpected token `fi' - shell

Getting syntax error near unexpected token `fi' while executing below code.
below is the output:-
: command not founde 2:
: command not founde 7:
run_billing.sh: line 22: syntax error near unexpected token `fi'
'un_billing.sh: line 22: `fi
The script is:
#!/bin/sh
#
# main
#
NARGS=$#
if [ $NARGS -lt 2 ]
then
echo "$PRG_NAME: error: incorrect number of arguments ($NARGS)";
echo "Usage: $PRG_NAME [Time]";
echo "Time format - pin_virtual_time format. e.g:- 062000002015.00";
echo "Example: sh run_billing.sh 062000002015.00";
exit 1
fi
if [ $NARGS -eq 2 ]
then
echo "Run Billing script - pin_bill_day";
pin_virtual_time -m2 $1;
pin_bill_day;
fi

Your line endings are screwed up, each line is terminated by a CR/LF rather than just an LF.
If you do something like od -c run_billing.sh, you'll see them there as \r characters as per my test script (the ^M characters are CR):
if [[ 1 -eq 1 ]]^M
then^M
echo x is 1^M
fi^M
^M
0000000 i f [ [ 1 - e q 1 ] ]
0000020 \r \n t h e n \r \n \t e c h o x
0000040 i s 1 \r \n f i \r \n \r \n
0000054
And, when that file is run, we see a similar issue.
That's why you're getting the weird error output, because the CR is moving the cursor to the start of the line before continuing the message, overwriting some of what it's already output.
For example, lines 2 and 7 of your script (the supposedly blank lines) contain a single CR character which is interpreted as a command which doesn't exist. So, for line 2, you see (superimposed):
run_billing.sh: line 2:<CR>
: command not found
=========================== giving:
: command not founde 2:
exactly what you see.
You need to modify the file to remove those CR characters, a couple of ways of doing that are given in this excellent answer.

Related

Bash Square Bracket Expansion

So the official bash manual states that "For example, the regular expression ‘[0123456789]’ matches any single digit, whereas ‘[^()]’ matches any single character that is not an opening or closing parenthesis,", copied a link at the bottom of this question, for context.
So I tested it every which way I could think of, to try and do the "negate" part of this, but I could not get it to work:
$ cat test
a line
b line
c line
d line
$ grep [^abc] test
a line
b line
c line
d line
$ grep '[^abc]' test
a line
b line
c line
d line
$ grep '[^(abc)]' test
a line
b line
c line
d line
[$ grep [^(abc)] test
bash: syntax error near unexpected token `('
https://www.gnu.org/software/grep/manual/html_node/Character-Classes-and-Bracket-Expressions.html
I was expecting just line D to be shown
[^abc] matches any character which is not a, b, or c.
It matches a line because it includes , l, i, n, and e, none of which are the excluded characters.
To ensure that no character in the string matches any in the list you would need grep '^[^abc]*$' test
To stick with the bash tag on the question, here’s a pure Bash option that takes advantage of extglob (which is mostly enabled by default).
while IFS= read -r line; do
[[ "$line" = *([^abc]) ]] && printf '%s\n' "$line"
done < test

Displaying only single most recent line of a command's output

How can I print a command output like one from rm -rv * in a single line ? I think it would need \r but I can't figure out how.
I would need to have something like this :
From:
removed /path/file1
removed /path/file2
removed /path/file3
To : Line 1 : removed /path/file1
Then : Line 1 : removed /path/file2
Then : Line 1 : removed /path/file3
EDIT : I may have been misunderstood, I want to have the whole process beeing printing in a single same line, changing as the command outputs an another line (like removed /path/file123)
EDIT2 : The output is sometimes too long to be display in on line (very long path). I would need something that considers that problem too :
/very/very/very/long/path/to/a/very/very/very/far/file/with-a-very-very-very-long-name1
/very/very/very/long/path/to/a/very/very/very/far/file/with-a-very-very-very-long-name2
/very/very/very/long/path/to/a/very/very/very/far/file/with-a-very-very-very-long-name3
Here's a helper function:
shopt -s checkwinsize # ensure that COLUMNS is available w/ window size
oneline() {
local ws
while IFS= read -r line; do
if (( ${#line} >= COLUMNS )); then
# Moving cursor back to the front of the line so user input doesn't force wrapping
printf '\r%s\r' "${line:0:$COLUMNS}"
else
ws=$(( COLUMNS - ${#line} ))
# by writing each line twice, we move the cursor back to position
# thus: LF, content, whitespace, LF, content
printf '\r%s%*s\r%s' "$line" "$ws" " " "$line"
fi
done
echo
}
Used as follows:
rm -rv -- * 2>&1 | oneline
To test this a bit more safely, one might use:
for f in 'first line' 'second line' '3rd line'; do echo "$f"; sleep 1; done | oneline
...you'll see that that test displays first line for a second, then second line for a second, then 3rd line for a second.
If you want a "status line" result that is showing the last line output by the program where the line gets over-written by the next line when it comes out you can send the output for the command through a short shell while loop like this:
YourCommand | while read line ; do echo -n "$line"$' ...[lots of spaces]... \r' ; done
The [Lots of spaces] is needed in case a shorter line comes after a longer line. The short line needs to overwrite the text from the longer line or you will see residual characters from the long line.
The echo -n $' ... \r' sends a literal carriage return without a line-feed to the screen which moves the position back to the front of the line but doesn't move down a line.
If you want the text from your command to just be output in 1 long line, then
pipe the output of any command through this sed command and it should replace the carriage returns with spaces. This will put the output all on one line. You could change the space to another delimiter if desired.
your command | sed ':rep; {N;}; s/\n/ /; {t rep};'
:rep; is a non-command that marks where to go to in the {t rep} command.
{N;} will join the current line to the next line.
It doesn't remove the carriage return but just puts the 2 lines in the buffer to be used for following commands.
s/\n/ /; Says to replace the carriage return character with a space character. They space is between the second and third/ characters.
You may need to replace \r\n depending on if the file has line feeds. UNIX files don't unless they came from a pc and haven't been converted.
{t rep}; says that if the match was found in the s/// command then go to the :rep; marker.
This will keep joining lines, removing the \n, then jumping to :rep; until there are no more likes to join.

Unix case statement in OSX

I am trying to port some Windows bat files to Mac shell scripts
I get a syntax error when executing a file containing the following case statement:
case ${1} in
( C | c ) echo "hoera";;
where argument ${1} is given as 'C'
The message is:
-bash: even: line 6: syntax error near unexpected token `newline'
'bash: even: line 6: ` ( C | c ) echo "hoera";;
I think the syntax is correct according to the documentation. Where am I going wrong?
Syntax should be:
case $var in
CASE1) COMMAND-LIST;;
CASE2) COMMAND-LIST;;
esac
Each case consists of one or more shell patterns, separated by pipes - you don't need an opening parenthesis to match the closing one. Your example should instead be:
case ${1} in
C|c) echo "hoera";;
Or possibly:
case ${1} in
[Cc]) echo "hoera";;

Bash Script Array compare issue

I have the following code in my script:
RESULT=$(cora_cmd --input={connect hrdwtst01.campbellsci.com';' file-control $STATION_NAME stop-program';' bye';'})
declare y
IFS=$'\n' y=($RESULT)
echo ${y[2]}
if [ ${y[2]} == '-file-control,loggernet datalogger locked' ]; then
echo -e "\t\E[31;1m.. ERROR - Not able to stop the datalogger's program. ..\E[37;0m"
fi
echo "$RESULT"
The compare is not working and it never goes into the if statement. Any ideas?
Results of the set -x:
'++ cora_cmd '--input={connect' 'hrdwtst01.campbellsci.com;' file-control TS_CR850_PB_801 'stop-program;' 'bye;}'
+ RESULT='CoraScript 1, 13, 06 Beta
+connect,"coralib3.dll version 1, 7, 18 Beta"
'file-control,loggernet datalogger locked
+ declare y
+ IFS='
'
+ y=($RESULT)
' echo '-file-control,loggernet datalogger locked
-file-control,loggernet datalogger locked
' '!=' '-file-control,loggernet datalogger locked' ']'
+ echo -e '\t\E[31;1m.. ERROR - Not able to stop the datalogger'\''s program. ..\E[37;0m'
.. ERROR - Not able to stop the datalogger's program. ..'
Always quote your variables unless you know you want word splitting and globbing to be done on the expansion.
if [ "${y[2]}" = '-file-control,loggernet datalogger locked' ]; then
You can also use the built-in [[ syntax, which doesn't do word splitting of variables.
if [[ ${y[2]} = '-file-control,loggernet datalogger locked' ]]; then
So there's a stray ^M on there indicating that your cora_cmd output
includes DOS-style \r\n line endings, and you're only stripping off
the \n, leaving the carriage return character. That's why you're not
getting a match... – twalberg
twalberg correctly diagnosed the problem. The simple solution is of course to include \r in IFS:
IFS=$'\r\n' y=($RESULT)
Including the \r in the test == $'-file-control,loggernet datalogger locked\r' is also possible.

Reading a subset of the lines in a text file, with bash

I have a file
line a - this is line a
line b - this is line b
line c - this is line c
line d - this is line d
line e - this is line e
The question is: How can I output the lines starting from "line b" till "line d" using bash commands?
I mean, to obtain:
"line b - this is line b
line c - this is line c
line d - this is line d"
sed -n '/line b/,/line d/p' file
Your example is not enough to infer what you want in the general case, but assuming you want to remove the first and last line, you can simply use
tail -n+2 $filename | head -n-1
Here tail -n+2 prints all the lines starting from the second, and head -n-1 prints all the lines except the last.
for your set of sample data:
awk '/line b/,/line d/' file
Or
awk '/line d/{f=0;print}/line b/{f=1}f' file
If by bash, you mean actually bash alone, I can't help you. You really should be using the right tools for the job. If you mean standard UNIX utilities that you can call from bash, I would be using awk for that.
echo 'line a - this is line a
line b - this is line b
line c - this is line c
line d - this is line d
line e - this is line e' | awk '
BEGIN {e=0}
/^line b/ {e=1}
/^line d/ {if (e==1) {print;exit}}
{if (e==1) print}
'
This outputs:
line b - this is line b
line c - this is line c
line d - this is line d
The way it works is simple.
e is the echo flag, initially set to false (0).
when you find line b, set echo to true (1) - don't print yet. That will be handled by the last bullet point below.
when you find line d and echo is on, print it and exit.
when echo is on, print the line (this includes line b).
I've made an assumption here that you don't want to exit on a line d unless you're already echoing. If that's wrong, move the exit outside of the if statement for line d:
/^line d/ {if (e==1) print;exit}
Then, if you get a line d before your line b, it will just exit without echoing anything.
The "/^line X/"-type clauses can be made very powerful to match pretty well anything you can throw at it.
You can do it using bash alone, though I agree with Pax that using other tools is probably a better solution. Here's a bash-only solution:
while read line
do
t=${line#line b}
if test "$t" != "$line"
then
echo $line
while read line
do
echo $line
t=${line#line d}
if test "$t" != "$line"
then
exit 0
fi
done
fi
done
Another approach which depends on what you mean:
pcregrep -m 'line b - this is line b
line c - this is line c
line d - this is line d' file

Resources