I have converted one function from C language to Unix ksh script and below is the code
function greg_julian
{
typeset -i long_date=$1;
print "$scriptname ld $long_date" 1>&2; # Prints 20160121
set days -9999 0 31 59 90 120 151 181 212 243 273 304 334;
if [[ $long_date < 101 ]] then
return 0;
fi
typeset -i yy=$long_date/10000;
typeset -i mm=$long_date/100 % 100;
ymod=$(( $yy % 4 ))
if [[ $ymod -eq 0 && $mm -gt 2 && $yy -ne 0 ]] then
dm=1;
else
dm=0;
fi
echo $yy*1000 + ${days[$mm]} + $long_date % 100 + $dm;
}
I get an error as below. Tried removing space around %. but same error. I am Unable to figure out the error please help.
greg_julian[87]: %: is not an identifier
You can use an intermediate variable:
#!/bin/ksh
long_date=20160121
typeset -i yy=$long_date/10000;
typeset -i yymm=$long_date/100;
typeset -i mm=$yymm%100;
printf "%04d\n" $yy
printf "%02d\n" $mm
One way to calculate mathematical expressions in shell script is to use expr.
Can you, please, try it and make a feedback please?
My main question is how to split strings on the command line into parameters using a terminal command in Linux?
For example
on the command line:
./my program hello world "10 20 30"
The parameters are set as:
$1 = hello
$2 = world
$3 = 10 20 30
But I want:
$1 = hello
$2 = world
$3 = 10
$4 = 20
$5 = 30
How can I do it correctly?
You can reset the positional parameters $# by using the set builtin. If you do not double-quote $#, the shell will word-split it producing the behavior you desire:
$ cat my_program.sh
#! /bin/sh
i=1
for PARAM; do
echo "$i = $PARAM";
i=$(( $i + 1 ));
done
set -- $#
echo "Reset \$# with word-split params"
i=1
for PARAM; do
echo "$i = $PARAM";
i=$(( $i + 1 ));
done
$ sh ./my_program.sh foo bar "baz buz"
1 = foo
2 = bar
3 = baz buz
Reset $# with word-split params
1 = foo
2 = bar
3 = baz
4 = buz
As an aside, I find it mildly surprising that you want to do this. Many shell programmers are frustrated by the shell's easy, accidental word-splitting — they get "John", "Smith" when they wanted to preserve "John Smith" — but it seems to be your requirement here.
Use xargs:
echo "10 20 30" | xargs ./my_program hello world
xargs is a command on Unix and most Unix-like operating systems used
to build and execute command lines from standard input. Commands such as
grep and awk can accept the standard input as a parameter, or argument
by using a pipe. However, others such as cp and echo disregard the
standard input stream and rely solely on the arguments found after the
command. Additionally, under the Linux kernel before version 2.6.23,
and under many other Unix-like systems, arbitrarily long lists of
parameters cannot be passed to a command,[1] so xargs breaks the list
of arguments into sublists small enough to be acceptable.
(source)
I am having problem when trying to assign a value to a variable in a loop and trying to print it outside the loop using korn shell. I want to use that variable in later part of my script. So I am trying to test by printing the value of the dynamic variable. I just assigned to it from my array.
#!/usr/bin/ksh
clear
BINPATH=/usr/bin
SVR_LIST=servers_list
set -A SERVERS `cat $SVR_LIST`
typeset -i i=0
Sn=${#SERVERS[#]}
#echo "Number of servers in an array are .................." $Sn
while [ $i -lt ${#SERVERS[#]} ] ; do
#print ${SERVERS[$i]}
typeset -l s${i}=${SERVERS[$i]}
#eval echo "Value of Variable is" \${s$i}
#s=\${s$i}
(( i=i+1 ))
done
s=\${s$i}
eval echo "value of s is " $s
s=eval \${s0}
APPSERVER1=$s
echo $APPSERVER1
s=eval \${s1}
APPSERVER2=$s
echo $APPSERVER2
I am getting following error.
value of s is
./variableTest.sh[21]: ${s0}:not found
${s4}
./variableTest.sh[24]: ${s1}:not found
${s4}
here is working code…
#!/bin/ksh
clear
#BINPATH=/usr/bin
SVR_LIST=test
set -A SERVERS `cat $SVR_LIST`
typeset -i i=0
Sn=${#SERVERS[#]}
#echo "Number of servers in an array are .................." $Sn
while [ $i -lt ${#SERVERS[#]} ] ; do
#print ${SERVERS[$i]}
typeset -l s${i}=${SERVERS[$i]}
eval echo "Value of Variable is" \${s$i}
#s=\${s$i}
(( i=i+1 ))
done
#NOTE THIS LINE i value is i+1 here..
#so if you had last variable as s10=abc you are using s11 outside the loop in follwoing two lines..now its s10
let i=i-1
s=\${s$i}
eval echo "value of s is " $s
#CHANGES HERE
s=$s0
APPSERVER1=$s
echo $APPSERVER1
s=$s1
APPSERVER2=$s
echo $APPSERVER2
output...
Value of Variable is "credit":
Value of Variable is "wic":
Value of Variable is "wiccash":
Value of Variable is "sfmnp":
Value of Variable is "snap":
Value of Variable is "baked goods":
Value of Variable is "baked goods":
"credit":
"wic":
for example
a=1;b=2;c=3;d=4;e=5
the file is 1.sh
cat 1.sh
echo $a
echo $b
echo $c
echo $d
echo $e
i use the command
source 1.sh
i get
1
2
3
4
5
how can i source the last 3 line of 1.sh?
tail -n 3 1.sh|xargs -0 sh -c
can not work because the variable can not pass to sh
tail -n 3 1.sh > tmp.sh ; source ./tmp.sh ; rm tmp.sh
If necessary, choose or generate a name that's unlikely to collide with anything else, like /tmp/tmp$$.sh.
I am working on a UNIX box, and trying to run an application, which gives some debug logs to the standard output. I have redirected this output to a log file, but now wish to get the lines where the error is being shown.
My problem here is that a simple
cat output.log | grep FAIL
does not help out. As this shows only the lines which have FAIL in them. I want some more information along with this. Like the 2-3 lines above this line with FAIL. Is there any way to do this via a simple shell command? I would like to have a single command line (can have pipes) to do the above.
grep -C 3 FAIL output.log
Note that this also gets rid of the useless use of cat (UUOC).
grep -A $NUM
This will print $NUM lines of trailing context after matches.
-B $NUM prints leading context.
man grep is your best friend.
So in your case:
cat log | grep -A 3 -B 3 FAIL
I have two implementations of what I call sgrep, one in Perl, one using just pre-Perl (pre-GNU) standard Unix commands. If you've got GNU grep, you've no particular need of these. It would be more complex to deal with forwards and backwards context searches, but that might be a useful exercise.
Perl solution:
#!/usr/perl/v5.8.8/bin/perl -w
#
# #(#)$Id: sgrep.pl,v 1.6 2007/09/18 22:55:20 jleffler Exp $
#
# Perl-based SGREP (special grep) command
#
# Print lines around the line that matches (by default, 3 before and 3 after).
# By default, include file names if more than one file to search.
#
# Options:
# -b n1 Print n1 lines before match
# -f n2 Print n2 lines following match
# -n Print line numbers
# -h Do not print file names
# -H Do print file names
use strict;
use constant debug => 0;
use Getopt::Std;
my(%opts);
sub usage
{
print STDERR "Usage: $0 [-hnH] [-b n1] [-f n2] pattern [file ...]\n";
exit 1;
}
usage unless getopts('hnf:b:H', \%opts);
usage unless #ARGV >= 1;
if ($opts{h} && $opts{H})
{
print STDERR "$0: mutually exclusive options -h and -H specified\n";
exit 1;
}
my $op = shift;
print "# regex = $op\n" if debug;
# print file names if -h omitted and more than one argument
$opts{F} = (defined $opts{H} || (!defined $opts{h} and scalar #ARGV > 1)) ? 1 : 0;
$opts{n} = 0 unless defined $opts{n};
my $before = (defined $opts{b}) ? $opts{b} + 0 : 3;
my $after = (defined $opts{f}) ? $opts{f} + 0 : 3;
print "# before = $before; after = $after\n" if debug;
my #lines = (); # Accumulated lines
my $tail = 0; # Line number of last line in list
my $tbp_1 = 0; # First line to be printed
my $tbp_2 = 0; # Last line to be printed
# Print lines from #lines in the range $tbp_1 .. $tbp_2,
# leaving $leave lines in the array for future use.
sub print_leaving
{
my ($leave) = #_;
while (scalar(#lines) > $leave)
{
my $line = shift #lines;
my $curr = $tail - scalar(#lines);
if ($tbp_1 <= $curr && $curr <= $tbp_2)
{
print "$ARGV:" if $opts{F};
print "$curr:" if $opts{n};
print $line;
}
}
}
# General logic:
# Accumulate each line at end of #lines.
# ** If current line matches, record range that needs printing
# ** When the line array contains enough lines, pop line off front and,
# if it needs printing, print it.
# At end of file, empty line array, printing requisite accumulated lines.
while (<>)
{
# Add this line to the accumulated lines
push #lines, $_;
$tail = $.;
printf "# array: N = %d, last = $tail: %s", scalar(#lines), $_ if debug > 1;
if (m/$op/o)
{
# This line matches - set range to be printed
my $lo = $. - $before;
$tbp_1 = $lo if ($lo > $tbp_2);
$tbp_2 = $. + $after;
print "# $. MATCH: print range $tbp_1 .. $tbp_2\n" if debug;
}
# Print out any accumulated lines that need printing
# Leave $before lines in array.
print_leaving($before);
}
continue
{
if (eof)
{
# Print out any accumulated lines that need printing
print_leaving(0);
# Reset for next file
close ARGV;
$tbp_1 = 0;
$tbp_2 = 0;
$tail = 0;
#lines = ();
}
}
Pre-Perl Unix solution (using plain ed, sed, and sort - though it uses getopt which was not necessarily available back then):
#!/bin/ksh
#
# #(#)$Id: old.sgrep.sh,v 1.5 2007/09/15 22:15:43 jleffler Exp $
#
# Special grep
# Finds a pattern and prints lines either side of the pattern
# Line numbers are always produced by ed (substitute for grep),
# which allows us to eliminate duplicate lines cleanly. If the
# user did not ask for numbers, these are then stripped out.
#
# BUG: if the pattern occurs in in the first line or two and
# the number of lines to go back is larger than the line number,
# it fails dismally.
set -- `getopt "f:b:hn" "$#"`
case $# in
0) echo "Usage: $0 [-hn] [-f x] [-b y] pattern [files]" >&2
exit 1;;
esac
# Tab required - at least with sed (perl would be different)
# But then the whole problem would be different if implemented in Perl.
number="'s/^\\([0-9][0-9]*\\) /\\1:/'"
filename="'s%^%%'" # No-op for sed
f=3
b=3
nflag=no
hflag=no
while [ $# -gt 0 ]
do
case $1 in
-f) f=$2; shift 2;;
-b) b=$2; shift 2;;
-n) nflag=yes; shift;;
-h) hflag=yes; shift;;
--) shift; break;;
*) echo "Unknown option $1" >&2
exit 1;;
esac
done
pattern="${1:?'No pattern'}"
shift
case $# in
0) tmp=${TMPDIR:-/tmp}/`basename $0`.$$
trap "rm -f $tmp ; exit 1" 0
cat - >$tmp
set -- $tmp
sort="sort -t: -u +0n -1"
;;
*) filename="'s%^%'\$file:%"
sort="sort -t: -u +1n -2"
;;
esac
case $nflag in
yes) num_remove='s/[0-9][0-9]*://';;
no) num_remove='s/^//';;
esac
case $hflag in
yes) fileremove='s%^$file:%%';;
no) fileremove='s/^//';;
esac
for file in $*
do
echo "g/$pattern/.-${b},.+${f}n" |
ed - $file |
eval sed -e "$number" -e "$filename" |
$sort |
eval sed -e "$fileremove" -e "$num_remove"
done
rm -f $tmp
trap 0
exit 0
The shell version of sgrep was written in February 1989, and bug fixed in May 1989. It then remained unchanged except for an administrative change (SCCS to RCS transition) in 1997 until 2007, when I added the -h option. I switched to the Perl version in 2007.
http://thedailywtf.com/Articles/The_Complicator_0x27_s_Gloves.aspx
You can use sed to print specific lines, lets say you want line 20
sed '20 p' -n FILE_YOU_WANT_THE_LINE_FROM
Done.
-n prevents echoing lines from the file. The part in quotes is a sed rule to apply, it specifies that you want the rule to apply to line 20, and you want to print.
With GNU grep on Windows:
$ grep --context 3 FAIL output.log
$ grep --help | grep context
-B, --before-context=NUM print NUM lines of leading context
-A, --after-context=NUM print NUM lines of trailing context
-C, --context=NUM print NUM lines of output context
-NUM same as --context=NUM