Parsing string for retrieving date in UNIX and freeBSD - sh

I have a string in following format:
YYYY-MM-DD
how can i convert it to date on UNIX and freeBSD?
I know it is
date -d $VAR +'%Y-%m-%d' on GNU
and
date -jf $VAR +'%Y-%m-%d' on freeBSD
But what if my script (in sh, not in bash) have to work on both OS? How can I combine it?

Because date command is different a kind of solution might be if detect correct platform:
#!/bin/sh
VAR='2014-03-15'
platform='uknown'
str="$(uname)"
if [ "$str" == 'Linux' ]; then
platform='linux'
elif [ "$str" == 'FreeBSD' ]; then
platform='freebsd'
fi
Then according to the platform you can do:
if [ "$platform" == 'linux' ]; then
date -d "$VAR" +'%Y-%m-%d'
elif [ "$platform" == 'freebsd' ]; then
date -jf "%Y-%m-%d" "$VAR" +'%Y-%m-%d'
fi
Also I think you are missing format for the command:
date -jf $VAR +'%Y-%m-%d'
I think i should be:
date -jf "format" $VAR +'%Y-%m-%d'

Related

awk on a date variable

What is working :
echo "Oct 12 2021" | awk '{print "date -d\""$1FS$2FS$3"\" +%Y%m%d"}' | bash
how could I use a variable on a bash script ?
mydate="Oct 12 2021"
awk -v dateformat= ''$mydate" '{print "date -d\""$1FS$2FS$3"\" +%Y%m%d"}'
You obviously wouldn't really use awk to call date on a single value but I assume you have something more than that in mind in your real code so this is what you asked for (call GNU date from awk using an awk variable as the date):
mydate="Oct 12 2021"
awk -v date="$mydate" 'BEGIN {
system("date -d\047" date "\047 +\047%Y%m%d\047")
}'
20211012
or if you prefer:
awk -v date="$mydate" 'BEGIN {
cmd = "date -d\047" date "\047 +\047%Y%m%d\047"
print ( (cmd | getline line) > 0 ? line : date"=N/A" )
close(cmd)
}'
20211012
Don't do either of those though. It's very slow to call an external command from awk and you don't need to when all you want to do is change text from one format to another:
$ awk -v date="$mydate" 'BEGIN {
split(date,d);
mth = (index("JanFebMarAprMayJunJulAugSepOctNovDec",d[1])+2)/3
printf "%04d%02d%02d\n", d[3], mth, d[2]
}'
20211012
Also, if you have GNU awk it has built-in time functions so even if what you wanted to do wasn't just shuffling bits of text around you could call the internal mktime() and strftime() instead of the external date:
awk -v date="$mydate" 'BEGIN {
split(date,d)
mth = (index("JanFebMarAprMayJunJulAugSepOctNovDec",d[1])+2)/3
secs = mktime(d[3]" "mth" "d[2]" 12 0 0")
print strftime("%Y%m%d",secs)
}'
20211012
Your original example uses awk to prepare arguments that are then passed to the Linux date command running in bash; the date formatting is performed by the date command.
You don't need awk for this, the date command -d option is very flexible in the format it receives.
mydate="Oct 3 2021"
date -d "$mydate" +%Y%m%d
Will give you a formatted date. You can assign the result to a variable using the $() syntax - run a command and assign result
formattedDate=$(date -d "$mydate" +%Y%m%d)
echo $formattedDate

posix sh: how to count number of occurrences in a string without using external tools?

In bash, it can be done like this:
#!/bin/bash
query='bengal'
string_to_search='bengal,toyger,bengal,persian,bengal'
delimiter='|'
replace_queries="${string_to_search//"$query"/"$delimiter"}"
delimiter_count="${replace_queries//[^"$delimiter"]}"
delimiter_count="${#delimiter_count}"
echo "Found $delimiter_count occurences of \"$query\""
Output:
Found 3 occurences of "bengal"
The caveat of course is that the delimiter cannot occur in 'query' or 'string_to_search'.
In POSIX sh, string replacement is not supported. Is there a way this can be done in POSIX sh using only shell builtins?
#!/bin/sh
query='bengal'
string_to_search='bengal,toyger,bengal,persian,bengal'
ct() (
n=0
IFS=,
q=$1
set $2
for t in "$#"; do
if [ "$t" = "$q" ]; then
n=$((n + 1))
fi
done
echo $n
)
n=$(ct "$query" "$string_to_search")
printf "found %d %s\n" $n $query
Though I'm not sure what the point is. If you've got a posix shell,
you also almost certainly have printf, sed, grep, and wc.
printf '%s\n' "$string_to_search" | sed -e 's/,/\n/g' | grep -Fx "$query" | wc -l
Think I got it...
#!/bin/sh
query='bengal'
string_to_search='bengal,toyger,bengal,persian,bengal'
i=0
process_string="$string_to_search"
while [ -n "$process_string" ]; do
case "$process_string" in
*"$query"*)
process_string="${process_string#*"$query"}"
i="$(( i + 1 ))"
;;
*)
break
;;
esac
done
echo "Found $i occurences of \"$query\""

how to use * in sh string comparison

How to correctly use * in sh? I tried googling it but couldn't find anything. The following echo ture. why is that?
file="test test"
if [ "$file" != "te"* ]
then
echo true
else
echo false
fi
To avoid all the potential problems, when using POSIX shell, you should consider using the old expr regex or match expressions. Your choices are:
#!/bin/sh
file="test test"
if [ $(expr "$file" : "te.*") -gt 0 ]
then
echo true
else
echo false
fi
or
if [ $(expr substr "$file" 1 2) = "te" ]
then
echo true
else
echo false
fi
Not elegant, but they are the proper tools for the shell. A short explanation of each and the expr syntax for each is:
string : regularExp : returns the length of string if both sides match,
returns 0 otherwise
match string regularExp : same as the previous one
substr string start length : returns the substring of string starting from
start and consisting of length characters
I did a bit of googling and found a good bash scripting resource:
Advanced Bash-Scripting Guide
There is a segment that answers your question:
[[ $a == z* ]] # True if $a starts with an "z" (pattern matching).
[[ $a == "z*" ]] # True if $a is equal to z* (literal matching).
[ $a == z* ] # File globbing and word splitting take place.
[ "$a" == "z*" ] # True if $a is equal to z* (literal matching).
So in your case the condition should be:
if [[ file != te* ]]

Different results with shell command in and out of a perl script

I have a perl script that needs to check for an empty directory on a remote machine. Using ksh I can get the following shell script to work:
ksh# ssh user#host '[ "$(ls -A /empty/dir/* 2>/dev/null)" ] && echo "1" || echo "0"'
This correctly returns a "0" if the directory is empty or does not exist. It returns a "1" only if the directory contains something.
When I place this line inside of the perl script though like so:
#!/usr/bin/perl
print `ssh user\#host '[ "$(ls -A /empty/dir/* 2>/dev/null)" ] && echo "1" || echo "0"'`
No matter what I put in there it returns a "1", empty directory or not. I've checked env values compared to the normal shell and the perl script and they are the same.
Does anyone have any ideas why this command would return different results only in the perl script?
Both machines are AIX 6.1 with KSH as the default shell.
Text inside backticks is interpolated as if it were inside double quotes before being passed to the OS. Run
print qq`ssh user\#host '[ "$(ls -A /empty/dir/* 2>/dev/null)" ] && echo "1" || echo "0"'`
to see exactly what string is getting passed to the OS. I'll bet you'll at least have to escape the $.
A safer and saner way is to build your command first and run it inside backticks later:
# q{...} does no interpolation
my $cmd = q{ssh user\#host '[ "$(ls -A /empty/dir/* 2>/dev/null)" ] && echo "1" || echo "0"'};
print `$cmd`;
use Net::SFTP::Foreign;
my $s = Net::SFTP::Foreign->new('user#host');
my $empty = 1;
if (my $d = $s->opendir('/empty/dir')) {
if (defined $s->readdir($d)) {
$empty = 0
}
}

protect pipe character when executing an inline script on a remote server with ssh

I need to execute a script on a remote server over ssh, I can't locate the script as a file on the remote server nor create files during the script process.
The script checks for a non existent or zero byte file, and if exists, checks if is outdated.
I've followed a thread here on SO and tried this:
myvar=$(ssh user#server <<EOF
myfile=/mnt/file.csv
if [ -s $myfile ]; then
filedate=$(stat -c %x $myfile|grep '[0-9\-]*' --max-count=1 -o);
yesterday=$(date --date 'now -1 day' --iso-8601);
if [ $filedate < $yesterday ]; then
echo '1 '$yesterday;
else
echo '0 ok';
fi
else
echo $(date --iso-8601);
fi
EOF
)
sadly, the pipe appears to be truncating the string or something, because the script returns
stat: too few arguments
maybe just cannot use "myfile" var declaration. Any suggestions?
Thanks in advance.
---- Edit: Clarifying answer:
keber-laptop:~ keberflores$ echo $myvar
keber-laptop:~ keberflores$ myvar=$(ssh user#server <<EOF
> myfile=/mnt/file.csv
> if [ -s \$myfile ]; then
> filedate=\$(stat -c %x \$myfile|grep '[0-9\-]*' --max-count=1 -o);
> yesterday=\$(date --date 'now -1 day' --iso-8601);
> if [ \$filedate < \$yesterday ]; then
> echo '1 '\$yesterday;
> else
> echo '0 ok';
> fi
> else
> echo '1 '\$(date --iso-8601);
> fi
> EOF
> )
Pseudo-terminal will not be allocated because stdin is not a terminal.
user#server's password:
keber-laptop:~ keberflores$ echo $?
0
keber-laptop:~ keberflores$ echo $myvar
1 2011-10-22
---- Edit: calling inside perl:
my $myvar = qx'ssh user#server <<\'EOF\'
myfile=/mnt/file.csv
if [ -s $myfile ]; then
filedate=$(stat -c %x $myfile|grep \'[0-9\-]*\' --max-count=1 -o);
yesterday=$(date --date \'now -1 day\' --iso-8601);
if [ $filedate < $yesterday ]; then
echo \'1 \'$yesterday;
else
echo \'0 ok\';
fi
else
echo \'1 \'$(date --iso-8601);
fi
EOF
';
print $myvar;
You need more escaping to get this to work. bash is going to evaluate the variables, etc in the here-file locally, then the result of that will be evaluated on the remote server.
In particular, your $myfile is probably evaluating to an empty string when the here-file is being evaluated, causing the stat to not have a file argument.