Comparing version strings in Zsh - conditional-statements

I am using this script:
if [[ -f /usr/bin/atom ]]; then
ATOM_INSTALLED_VERSION=$(rpm -qi atom | grep "Version" | cut -d ':' -f 2 | cut -d ' ' -f 2)
else
ATOM_INSTALLED_VERSION=""
fi
ATOM_LATEST_VERSION=$(wget -q "https://api.github.com/repos/atom/atom/releases/latest" -O - | grep -E "https.*atom-amd64.tar.gz" | cut -d '"' -f 4 | cut -d '/' -f 8 | sed 's/v//g')
if [[ "$ATOM_INSTALLED_VERSION" -lt "$ATOM_LATEST_VERSION" ]]; then
sudo dnf install -y https://github.com/atom/atom/releases/download/v${ATOM_LATEST_VERSION}/atom.x86_64.rpm
fi
to check for Atom upgrades and install them if available. The problem is that the test:
[[ "$ATOM_INSTALLED_VERSION" -lt "$ATOM_LATEST_VERSION" ]]
returns:
zsh: bad floating point constant
where (showing input and output):
$ printf $ATOM_INSTALLED_VERSION
1.8.0%
$ printf $ATOM_LATEST_VERSION
1.12.7%
how do I write a test that will work? I have tried using (( $ATOM_INSTALLED_VERSION < $ATOM_LATEST_VERSION )) but that also failed giving:
zsh: bad floating point constant

zsh comes equipped with a function for version string comparisons, see zshcontrib(1).
installed=$(rpm -q --qf '%{VERSION}' atom)
latest=$(wget -q ...)
autoload is-at-least
is-at-least $latest ${installed:-0} || sudo dnf install -y ...

Related

How can I access a VPN inside a VMWare Fusion VM

I have a VPN connection in MacOS BigSur but I can't access it inside a Linux VM running under VMWare Fusion V12.1.2.
The issue has been fixed in V12.2.0 VMWare Fusion 12.2.0 Release Notes
The solution is to manually create the VPN tunnel and link it to the VM as there are multiple commands involved and the IP Address can change I created the following script to execute the required commands.
#!/bin/bash
function ask_yes_or_no() {
read -p "$1 ([y]es or [N]o): "
case $(echo $REPLY | tr '[A-Z]' '[a-z]') in
y|yes) echo "yes" ;;
*) echo "no" ;;
esac
}
currNatRules=$(sudo pfctl -a com.apple.internet-sharing/shared_v4 -s nat 2>/dev/null)
if test -z "$currNatRules"
then
echo -e "\nThere are currently no NAT rules loaded\n"
exit 0
fi
utunCheck=$(echo $currNatRules | grep utun)
if test -n "$utunCheck"
then
echo -e "\nIt looks like the VPN tunnel utun2 has already been created"
echo -e "\n$currNatRules\n"
if [[ "no" == $(ask_yes_or_no "Do you want to continue?") ]]
then
echo -e "\nExiting\n"
exit 0
fi
fi
natCIDR=$(echo $currNatRules | grep en | grep nat | cut -d\ -f 6)
if test -z "$natCIDR"
then
echo -e "\nCannot extract the NAT CIDR from:"
echo -e "\n$currNatRules\n"
exit 0
fi
interface=$(route get 10/8 | grep interface | cut -d\ -f 4)
echo -e "\nNAT CIDR=$natCIDR Interface=$interface\n"
newRule="nat on ${interface} inet from ${natCIDR} to any -> (${interface}) extfilter ei"
echo -e "\nAdding new rule: $newRule\n"
configFile="fixnat_rules.conf"
[[ -d $configFile ]] && rm $configFile
echo "$currNatRules" > $configFile
echo "$newRule" >> $configFile
sudo pfctl -a com.apple.internet-sharing/shared_v4 -N -f ${configFile} 2>/dev/null
echo -e "\nConfig update applied\n"
sudo pfctl -a com.apple.internet-sharing/shared_v4 -s nat 2>/dev/null
echo -e "\n"
exit 0

grep pattern match with line number

I want to get the line number of the matched pattern but I have a condition that the pattern match should have 'digits' .
If I use
grep -ri -n "package $i " . | grep -P '\d'
then I would get the line number of the lines matching pattern but also I would get the lines with 'package ' without any digits:
Below output shows me line number 71 for 'package ca-certificates' but there are four more lines for gluterfs that I dont need . I dont need those lines as they dont have any digit in them .
for i in $(awk '{print $1}' ~/Version-pkgs)
do
grep -ri -n "package $i " . | grep -P '\d'
done
sh search-version-pkgs.sh
./core.pkglist:71:package ca-certificates 2017.2.14 65.0.1.el6_9 arch noarch
./dev.pkglist:1343:package glusterfs-devel \
./dev.pkglist:1346:package glusterfs-api-devel \
./dev.pkglist:1346:package glusterfs-api-devel \
./dev.pkglist:1346:package glusterfs-api-devel \
./dev.pkglist:1343:package glusterfs-devel \
./core.pkglist:234:package initscripts 9.03.58 1.0.3.el6_9.2prerel7.6.0.0.0_88.51.0 arch ${bestArch}
./core.pkglist:397:package nspr 4.13.1 1.el6
./dev.pkglist:859:package nspr-devel \
./dev.pkglist:859:package nspr-devel \
./core.pkglist:401:package nss 3.28.4 4.0.1.el6_9 arch ${bestArch}
Running below script gives me exact pattern match i.e. 'package ' but I would not get the line number of them
for i in $(awk '{print $1}' ~/Version-pkgs)
do
egrep -ri "package $i " . | grep -P '\d'
done
sh search-version-pkgs.sh
./core.pkglist:package ca-certificates 2017.2.14 65.0.1.el6_9 arch noarch
./core.pkglist:package initscripts 9.03.58 1.0.3.el6_9.2prerel7.6.0.0.0_88.51.0 arch ${bestArch}
./core.pkglist:package nspr 4.13.1 1.el6
./core.pkglist:package nss 3.28.4 4.0.1.el6_9 arch ${bestArch}
./core.pkglist:package nss-util 3.28.4 1.el6_9 arch ${bestArch}
./core.pkglist:package tzdata 2018e 3.el6 arch noarch
How can get the output with the line number along with the pattern match as file:lineno.:package pkgname digits
for i in $(cut -f1 ~/Version-pkgs)
do
grep -rin "package $i.*[0-9]" .
done
no need to use grep twice
Oneliner :
grep -rinf <(sed -E 's,([^ ]*).*,package \1.*[0-9],' ~/Version-pkgs) .

How to execute a remote command over ssh?

I try to connect to the remote server by ssh and execute the command.
But given the situation, I can only execute a single command.
For example
ssh -i ~/auth/aws.pem ubuntu#server "echo 1"
It works very well, but I have a problem with the following
case1
ssh -i ~/auth/aws.pem ubuntu#server "cd /"
ssh -i ~/auth/aws.pem ubuntu#server "ls"
case2
ssh -i ~/auth/aws.pem ubuntu#server "export a=1"
ssh -i ~/auth/aws.pem ubuntu#server "echo $a"
The session is not maintained.
Of course, you can use "cd /; ls"
but I can only execute one command at a time.
...
Reflecting comments
developed a bash script
function cmd()
{
local command_delete="$#"
if [ -f /tmp/variables.current ]; then
set -a
source /tmp/variables.current
set +a
cd $PWD
fi
if [ ! -f /tmp/variables.before ]; then
comm -3 <(declare | sort) <(declare -f | sort) > /tmp/variables.before
fi
echo $command_delete > /tmp/export_command.sh
source /tmp/export_command.sh
comm -3 <(declare | sort) <(declare -f | sort) > /tmp/variables.after
diff /tmp/variables.before /tmp/variables.after \
| sed -ne 's/^> //p' \
| sed '/^OLDPWD/ d' \
| sed '/^PWD/ d' \
| sed '/^_/ d' \
| sed '/^PPID/ d' \
| sed '/^BASH/ d' \
| sed '/^SSH/ d' \
| sed '/^SHELLOPTS/ d' \
| sed '/^XDG_SESSION_ID/ d' \
| sed '/^FUNCNAME/ d' \
| sed '/^command_delete/ d' \
> /tmp/variables.current
echo "PWD=$(pwd)" >> /tmp/variables.current
}
ssh -i ~/auth/aws.pem ubuntu#server "cmd cd /"
ssh -i ~/auth/aws.pem ubuntu#server "cmd ls"
What better solution?
$ cat <<'EOF' | ssh user#server
export a=1
echo "${a}"
EOF
Pseudo-terminal will not be allocated because stdin is not a terminal.
user#server's password:
1
In this way you will send all commands to ssh as a single file script, so you can put any number of commands. Please note the way to use EOF between single quote '.

A script to change file names

I am new to awk and shell based programming. I have a bunch of files name file_0001.dat, file_0002.dat......file_1000.dat. I want to change the file names such as the number after file_ will be a multiple of 4 in comparison to previous file name. SO i want to change
file_0001.dat to file_0004.dat
file_0002.dat to file_0008.dat
and so on.
Can anyone suggest a simple script to do it. I have tried the following but without any success.
#!/bin/bash
a=$(echo $1 sed -e 's:file_::g' -e 's:.dat::g')
b=$(echo "${a}*4" | bc)
shuf file_${a}.dat > file_${b}.dat
This script will do that trick for you:
#!/bin/bash
for i in `ls -r *.dat`; do
a=`echo $i | sed 's/file_//g' | sed 's/\.dat//g'`
almost_b=`bc -l <<< "$a*4"`
b=`printf "%04d" $almost_b`
rename "s/$a/$b/g" $i
done
Files before:
file_0001.dat file_0002.dat
Files after first execution:
file_0004.dat file_0008.dat
Files after second execution:
file_0016.dat file_0032.dat
Here's a pure bash way of doing it (without bc, rename or sed).
#!/bin/bash
for i in $(ls -r *.dat); do
prefix="${i%%_*}_"
oldnum="${i//[^0-9]/}"
newnum="$(printf "%04d" $(( 10#$oldnum * 4 )))"
mv "$i" "${prefix}${newnum}.dat"
done
To test it you can do
mkdir tmp && cd $_
touch file_{0001..1000}.dat
(paste code into convert.sh)
chmod +x convert.sh
./convert.sh
Using bash/sed/find:
files=$(find -name 'file_*.dat' | sort -r)
for file in $files; do
n=$(sed 's/[^_]*_0*\([^.]*\).*/\1/' <<< "$file")
let n*=4
nfile=$(printf "file_%04d.dat" "$n")
mv "$file" "$nfile"
done
ls -r1 | awk -F '[_.]' '{printf "%s %s_%04d.%s\n", $0, $1, 4*$2, $3}' | xargs -n2 mv
ls -r1 list file in reverse order to avoid conflict
the second part will generate new filename. For example: file_0002.dat will become file_0002.dat file_0008.dat
xargs -n2 will pass two arguments every time to mv
This might work for you:
paste <(seq -f'mv file_%04g.dat' 1000) <(seq -f'file_%04g.dat' 4 4 4000) |
sort -r |
sh
This can help:
#!/bin/bash
for i in `cat /path/to/requestedfiles |grep -o '[0-9]*'`; do
count=`bc -l <<< "$i*4"`
echo $count
done

AccuRev: how do you list the managed files?

I need to see which files have been added or removed between two streams. The most obvious way would be "git lsfiles" in each stream. Except this is not GIT and I do not see an analogous command. So for today:
for f in $(find * -type f);do
accurev stat "$f"
done | \
fgrep -v '(external)' | \
awk '{print $1}' > .list
If there is a better way, it should be clear and easy to find here:
http://www.accurev.com/download/docs/5.7.0_books/AccuRev_5_7_User_CLI.pdf
but it is not. Help? Thank you.
If you want to see the difference between two streams, run the following command: accurev diff -a -v "Stream1" -V "Stream2"
As the command line question has been answered, here's how to do the same via the AccuRev GUI.
Select one dynamic stream, workspace or snapshot.
Right click and select "Show Diff By Files"
Select a different dynamic stream, workspace or snapshot.
You'll be presented with a list of files different between the two choices, and yes you can mix-and-match between dynamic streams, workspaces and snapshots.
You can then select any file and select "Show Difference" to see differences between the two files.
Since neither of the two answers addressed the question, I eventually worked out a script to do what is really needed. "accurev lsfiles" is sorely needed.
#! /bin/bash
declare -r progpid=$$
declare -r progdir=$(cd $(dirname $0) >/dev/null && pwd)
declare -r prog=$(basename $0)
declare -r program="$progdir/$prog"
declare -r usage_text=' [ <directory> ... ]
If no directory is specified, "." is assumed'
die() {
echo "$prog error: $*"
exec 1>/dev/null 2>&1
kill -9 $progpid
exit 1
} 1>&2
usage() {
test $# -gt 0 && {
exec 1>&2
echo "$prog usage error: $*"
}
printf "USAGE: $prog %s\n" "$usage_text"
exit $#
}
init() {
shift_ct=$#
tmpd=$(mktemp -d ${TMPDIR:-/tmp}/ls-XXXXXX)
test -d "$tmpd" || die "mktemp -d does not work"
exec 4> ${tmpd}/files
trap "rm -rf '$tmpd'" EXIT
prune_pat=
while :
do
test $# -eq 0 && break
test -f "$1" && break
[[ "$1" =~ -.* ]] || break
case "X$1" in
X-p )
prune_pat+="${2}|"
shift 2 || usage "missing arg for '-p' option"
;;
X-p* )
prune_pat+="${1#-p}"
shift
;;
X-x* )
set -x
tput reset 1>&2
PS4='>${FUNCNAME:-lsf}> '
shift
;;
X-* )
usage "unknown option: $1"
;;
* )
break
;;
esac
done
(( shift_ct -= $# ))
test ${#prune_pat} -gt 0 && \
prune_pat='(^|/)('${prune_pat%|}')$'
}
chkdir() {
declare list=$(exec 2> /dev/null
for f in "$#"
do ls -d ${f}/* ${f}/.*
done | \
grep -v -E '.*/\.\.*$' )
for f in $(accurev stat ${list} | \
grep -v -F '(external)' | \
awk '{print $1}' | \
sed 's#^/*\./##')
do
test ${#prune_pat} -gt 0 && [[ $f =~ ${prune_pat} ]] && continue
if test -d "$f"
then chkdir "$f"
elif test -f "$f" -o -L "$f"
then echo "$f" 1>&4
fi
done
}
init ${1+"$#"}
(( shift_ct > 0 )) && shift ${shift_ct}
test $# -eq 0 && set -- .
chkdir "$#"
exec 4>&-
sort -u ${tmpd}/files
It is a bit over-the-top, but I have a boilerplate I always use for my scripts.