How do I determine what shell a script was meant to use? - shell

I have this non-functioning script, before I can attempt to fix it I need to know what kind of shell this was supposed to run in.
#
set pwrs = ( 0 0 0 0 0 0 0 0 )
# pwrs[1]=1
# next=2
while ( $next < 9 )
# last = $next - 1
# pwrs[$next] = $pwrs[$last] * 2
# next = $next + 1
end
#count = 1
while ( $count <= 8 )
echo $pwrs[$count]
# count = $count + 1
end

I was able to get this to produce appropriate output using csh. I would expect tcsh to work as well.
Turns out I do have a csh available. I pasted the code into the cmdline.
There was one error. All math calculations that begin with the # char must be separated from their following variable name, hence
#count = 1
Needed to be fixed as
# count = 1
$ set pwrs = ( 0 0 0 0 0 0 0 0 )
$ # pwrs[1]=1
$ # next=2
$ while ( $next < 9 )
while? # last = $next - 1
while? # pwrs[$next] = $pwrs[$last] * 2
while? # next = $next + 1
while? end
$ # count = 1
$ while ( $count <= 8 )
while? echo $pwrs[$count]
while? # count = $count + 1
while? end
output
1
2
4
8
16
32
64
128
[oracle#localhost ~]$
See Grymoire Unix - Csh for explanation of the csh #, and $pwrs[$next] (array notation). Your specific case is not addressed, but you should be able to work it out. If you have other questions after you build small test cases that don't work, post them with sample inputs, expected outputs, and your current code and output.
Also, don't spend any more time on csh, while not as bad as some portray it, you will ultimately discover that there are problems you can't solve in csh. All of it's nice cmd-line features are included in bash, so you might want to consider using it.Finally, see Grymoire csh top 10 for detailed reasons why you want to convert this code to a newer shell.
IHTH

Related

Retry a command only once : when a command fails (in bash)

for ( i=3; i<5; i++)
do
execute some command 1
if command 2 is successful then do not run the command 1 (the for loop should continue)
if command 2 is not successful then run command 1 only once (like retry command 1 only once, after this the for loop should continue)
done
This is to note that command 2 is dependent on command 1 and command 2 can only be executed after command 1
for example:
for ( i=3; i<5; i++)
do
echo "i" >> mytext.txt ---> command 1
if "check the content of mytext.txt file to see if the value of i is actually added" ---> command 2
if it is not added then execute echo "i" >> mytext.txt (command 1) again and only once.
if i value is added to the file .. then exit and continue the loop
done
Since the "command 1" is quite big and not just an example echo statement here.I do not want to add "command 1" twice .. once outside and once inside the if condition. I want this logic in an optimized way with no redundancy of code.
Per a comment it sounds like the OP may need to invoke command 1 up to 2 times for a given $i value, but only wants to type command 1 once in the script.
Siddhartha's suggestion to use a function is probably good enough but depending on the actual command 1 (OP mentions that it's 'quite big') I'm going to play devil's advocate and assume there could be additional issues with passing some args to the function (eg, a need to escape some characters ... ??).
The general idea is to have an internal loop that can be executed at most 2 times, with logic in the loop that will allow for an 'early' exit (eg, after just one pass through the loop).
Since we're using pseudo-code I'll use the same ...
for ( i=3; i<5; i++ )
do
pass=1 # reset internal loop counter
while ( pass -le 2 )
do
echo "i" >> mytext.txt # command 1
if ( pass -eq 1 ) # after first 'command 1' execution
&& ( value of 'i' is in mytext.txt ) # command 2
then
break # break out of inner loop; alternatively ...
# pass=10 # ensure pass >= 2 to force loop to exit on this pass
fi
pass=pass+1 # on 1st pass set pass=2 => allows another pass through loop
# on 2nd pass set pass=3 => will force loop to exit
done
done
you can declare functions like
function command
{
your_command -f params
}
for ( i=3; i<5; i++)
do
if command ; then
echo "success"
else
echo "retry"
command
fi
done

Script usage for output

I am having 230 directories(*_t) where I need to grep "Unseen Issues" in report.rt file in all directories.
I have tried this :
grep -r "Unseen Issues" *_t/A_*/ar.rt
I got this where pf_t, pu_t, pv_t, pz_t are directories:
pf_t/A_output/ar.rt:Number of Unseen Issues = 3
pf_t/A_output/ar.rt:adsd1p2r 50 Unseen Issues ( 1 )
pf_t/A_output/ar.rt:edsd1p2r 50 Unseen Issues ( 1 )
pf_t/A_output/ar.rt:wdsd1p2r 50 Unseen Issues ( 1 )
pu_t/A_output/ar.rt:Number of Unseen Issues = 0
pv_t/A_output/ar.rt:Number of Unseen Issues = 0
pz_t/A_output/ar.rt:Number of Unseen Issues = 0
But I need the output in this way below:
pf_t
Number of Unseen Issues = 3
adsd1p2r 50 Unseen Issues ( 1 )
edsd1p2r 50 Unseen Issues ( 1 )
wdsd1p2r 50 Unseen Issues ( 1 )
pu_t
Number of Unseen Issues = 0
pv_t
Number of Unseen Issues = 0
pz_t
Number of Unseen Issues = 0
Can anyone please help me with any small script to get the output as above with using above grep command.
We can use any script can anyone please help me.
I would recommend using AWK if the intermediate text file test1.txt is important. Otherwise a shell loop is simple:
for d in *_t; do
echo "$d"
grep -h "Unseen Issues" $d/A_*/ar.rt
echo ""
done
Added a test case and ran the script from command-line:
$ find * -type f
pf_t/A_output/ar.rt
pu_t/A_output/ar.rt
pv_t/A_output/ar.rt
pz_t/A_output/ar.rt
$ for d in *_t; do echo "$d"; grep -h "Unseen Issues" $d/A_*/ar.rt; echo ""; done
pf_t
Number of Unseen Issues = 3
adsd1p2r 50 Unseen Issues ( 1 )
edsd1p2r 50 Unseen Issues ( 1 )
wdsd1p2r 50 Unseen Issues ( 1 )
pu_t
Number of Unseen Issues = 0
pv_t
Number of Unseen Issues = 0
pz_t
Number of Unseen Issues = 0

Bash. Return two function levels (two nested calls)

I need to know if Bash has some solution for my case. I need after some conditions to do a "double return". I mean, to perform a return of a function and also return the parent function to skip the rest of the code of that parent function.
I know that I can do a conditional using function return values to achieve this. But I'd like to know if in Bash exist something like "break 2" for functions. I don't want if possible to modify the code of the parent function because as you can imagine, in my real script there are dozens of functions and I don't want to modify all of them.
Example:
#!/bin/bash
function sublevelone() {
echo "sublevelone"
# Return 2, or break 2 or something :D
}
function main() {
sublevelone
echo "This is the part of the code to being avoid executed"
}
main
I don't know what the bash experts will think, but this works at least for simple cases:
multireturn(){
[ -n "$1" ] && poplevel="$1"
if [ "$poplevel" -ge 0 ]; then
trap multireturn DEBUG
shopt -s extdebug
(( poplevel-- ))
return 2
else
shopt -u extdebug
trap - DEBUG
return 0
fi
}
This makes use of the DEBUG trap and the extdebug flag:
extdebug
If set at shell invocation, arrange to execute the
debugger profile before the shell starts, identical to
the --debugger option. If set after invocation, behav-
ior intended for use by debuggers is enabled:
1. The -F option to the declare builtin displays the
source file name and line number corresponding to
each function name supplied as an argument.
2. If the command run by the DEBUG trap returns a
non-zero value, the next command is skipped and
not executed.
3. If the command run by the DEBUG trap returns a
value of 2, and the shell is executing in a sub-
routine (a shell function or a shell script exe-
cuted by the . or source builtins), the shell
simulates a call to return.
4. BASH_ARGC and BASH_ARGV are updated as described
in their descriptions above.
5. Function tracing is enabled: command substitu-
tion, shell functions, and subshells invoked with
( command ) inherit the DEBUG and RETURN traps.
6. Error tracing is enabled: command substitution,
shell functions, and subshells invoked with (
command ) inherit the ERR trap.
Example usage:
#!/bin/bash
multireturn(){
[ -n "$1" ] && poplevel="$1"
if [ "$poplevel" -ge 0 ]; then
trap multireturn DEBUG
shopt -s extdebug
(( poplevel-- ))
return 2
else
shopt -u extdebug
trap - DEBUG
return 0
fi
}
# define 8 levels of function calls
# (level N prints output, calls level N+1, then prints more output)
for i in $(seq 1 8); do
eval \
'level'$i'(){
echo -n " '$i'"
level'$((i+1))'
echo -n "('$i')"
}'
done
# final level calls multireturn
level9(){
echo -n " 9"
multireturn $n
echo -n "(9)"
}
# test various skip amounts
for i in $(seq 0 10); do
echo -n "$i:"
n=$i
level1
echo .
done
echo
echo done
Result:
0: 1 2 3 4 5 6 7 8 9(9)(8)(7)(6)(5)(4)(3)(2)(1).
1: 1 2 3 4 5 6 7 8 9(8)(7)(6)(5)(4)(3)(2)(1).
2: 1 2 3 4 5 6 7 8 9(7)(6)(5)(4)(3)(2)(1).
3: 1 2 3 4 5 6 7 8 9(6)(5)(4)(3)(2)(1).
4: 1 2 3 4 5 6 7 8 9(5)(4)(3)(2)(1).
5: 1 2 3 4 5 6 7 8 9(4)(3)(2)(1).
6: 1 2 3 4 5 6 7 8 9(3)(2)(1).
7: 1 2 3 4 5 6 7 8 9(2)(1).
8: 1 2 3 4 5 6 7 8 9(1).
9: 1 2 3 4 5 6 7 8 9.
10: 1 2 3 4 5 6 7 8 9
done
This is kinda whacky but if you use parentheses to define levelone, it will execute the function in a subshell and then you can exit out of that shell from an inner function. That said, I think it's more appropriate to use return to send back value that you check for in the parent function.
#!/bin/bash
function leveltwo() {
echo "two"
exit
}
function levelone() (
echo "one"
leveltwo
echo "three"
)
levelone
echo "four"
Will print:
one
two
four
Why don't you return an exit status from the function and add an if-statement to main like you normally would in any other programming/scripting language?
#!/bin/bash
function sublevelone() {
echo "sublevelone"
[ 0 -eq 1 ]
return $? # Returns 1 in this case because 0 != 1
}
function main() {
sublevelone
[ $? -eq 0 ] || return 1 # Return in case of error
echo "This is the part of the code to being avoid executed"
}
main
exit 0
Just a short version of #jhnc answer for exactly 2-level return from a function:
trap 'trap "shopt -u extdebug; trap - DEBUG; return 0" DEBUG; return 2' DEBUG
shopt -s extdebug
return

Perform a mask on variable

I have some inputs values (32-bit unsigned integer) defined as follow, with their associated identifier, in a file :
var=0x000000001
var1=0x000000002
var2=0x000000004
var3=0x000000008
toto=0x000000010
titi=0x000000020
tata=0x000000040
toto1=0x000000080
toto2=0x000000100
toto3=0x000000200
titi2=0x000000400
titi3=0x000000800
tito0=0x000001000
tito1=0x000002000
tito2=0x000004000
tito3=0x000008000
tito4=0x000010000
I would like to implement a second function name GetConfig() which must return an uint32 value. Based on this value, I would to quote the associated variable. If I enter 300 I should display toto2 toto3.
function Config()
{
vartest=$1
if [ -f $file ]; then
while read lines
do
value=${lines##*=}
mask=$(($vartest & $value))
echo $mask
done < $file
else
exitError 101
fi
}
If I enter ./script Config 3840 I obtain :
0
0
0
0
0
0
0
0
256
512
1024
2048
0
0
0
0
0
I can easily display toto2 toto3 titi5 titii, but if I enter ./script Config 4812, I obtain :
0
0
4
8
0
0
64
128
0
512
0
0
4096
0
0
0
0
This result can not allows me to display var1 titi titii test1
I am not sure to be clear but I am really currently blocked on this mask issue.
Thank in advance
Your problem statement isn't very clear but I hope this snippet can help put you back on track. The real meat is in using ${lines%=*} to obtain the key corresponding to the value in this lines.
Config() {
vartest=$1
if [ -f $file ]; then
while read lines
do
value=${lines##*=}
test $(($vartest & $value)) = 0 && continue
echo "${lines%=*}"
done < $file
else
exitError 101
fi
}
file=/dev/stdin
Config 3840 <<':'
var=0x000000001
var1=0x000000002
var2=0x000000004
var3=0x000000008
toto=0x000000010
titi=0x000000020
tata=0x000000040
toto1=0x000000080
toto2=0x000000100
toto3=0x000000200
titi2=0x000000400
titi3=0x000000800
tito0=0x000001000
tito1=0x000002000
tito2=0x000004000
tito3=0x000008000
tito4=0x000010000
:
Test run:
toto2
toto3
titi2
titi3
I would expect a more-useful function to not require the values in a file; this incidentally also illustrates how to put them in a here document (I did some weird stuff to not have to change those parts of your function).

How to validate a IPv6 address format with shell?

In my shell, I need check if a string is a valid IPv6 address.
I find two ways, neither of them is ideal enough to me.
One is http://twobit.us/2011/07/validating-ip-addresses/, while I wonder if it must be such complex for such a common requirement.
The other is expand ipv6 address in shell script, this is simple, but for major distribution of Linux, sipcalc isn't a common default utility.
So my question, is there a simple way or utility to validate a IPv6 address with shell?
Thanks in advance.
The code in the first link isn't particularly elegant, but modulo stylistic fixes, I don't think you can simplify much beyond that (and as indicated in a comment, it may already be too simple). The spec is complex and mandates a number of optional features, which is nice for the end user, but cumbersome for the implementor.
You could probably find a library for a common scripting language which properly encapsulates this logic in a library. My thoughts would go to Python, where indeed Python 3.3 includes a standard module called ipaddress; for older versions, try something like
#!/usr/bin/env python
import socket
import sys
try:
socket.inet_pton(socket.AF_INET6, sys.argv[1])
result=0
except socket.error:
result=1
sys.exit(result)
See also Checking for IP addresses
Here is a solution in POSIX compatible shell script that handles IPv4 and IPv6 addresses with an optional subnet mask. To test an IP that should not have a subnet mask just pass it a dummy one when performing the test. It seems like a lot of code but it should be significantly faster than using external programs like grep or scripts that are likely to fork.
Single IPv6 zero groups compressed to :: will be treated as invalid. The use of such representation is strongly discouraged, but technically correct. There is a note in the code explaining how to alter this behaivour if you wish to allow such addresses.
#!/bin/sh
set -e
# return nonzero unless $1 contains only digits, leading zeroes not allowed
is_numeric() {
case "$1" in
"" | *[![:digit:]]* | 0[[:digit:]]* ) return 1;;
esac
}
# return nonzero unless $1 contains only hexadecimal digits
is_hex() {
case "$1" in
"" | *[![:xdigit:]]* ) return 1;;
esac
}
# return nonzero unless $1 is a valid IPv4 address with optional trailing subnet mask in the format /<bits>
is_ip4() {
# fail if $1 is not set, move it into a variable so we can mangle it
[ -n "$1" ] || return
IP4_ADDR="$1"
# handle subnet mask for any address containing a /
case "$IP4_ADDR" in
*"/"* ) # set $IP4_GROUP to the number of bits (the characters after the last /)
IP4_GROUP="${IP4_ADDR##*"/"}"
# return failure unless $IP4_GROUP is a positive integer less than or equal to 32
is_numeric "$IP4_GROUP" && [ "$IP4_GROUP" -le 32 ] || return
# remove the subnet mask from the address
IP4_ADDR="${IP4_ADDR%"/$IP4_GROUP"}";;
esac
# backup current $IFS, set $IFS to . as that's what separates digit groups (octets)
IP4_IFS="$IFS"; IFS="."
# initialize count
IP4_COUNT=0
# loop over digit groups
for IP4_GROUP in $IP4_ADDR ;do
# return failure if group is not numeric or if it is greater than 255
! is_numeric "$IP4_GROUP" || [ "$IP4_GROUP" -gt 255 ] && IFS="$IP4_IFS" && return 1
# increment count
IP4_COUNT=$(( IP4_COUNT + 1 ))
# the following line will prevent the loop continuing to run for invalid addresses with many occurrences of .
# this makes no difference to the result, but may improve performance when validating many such invalid strings
[ "$IP4_COUNT" -le 4 ] || break
done
# restore $IFS
IFS="$IP4_IFS"
# return success if there are 4 digit groups, otherwise return failure
[ "$IP4_COUNT" -eq 4 ]
}
# return nonzero unless $1 is a valid IPv6 address with optional trailing subnet mask in the format /<bits>
is_ip6() {
# fail if $1 is not set, move it into a variable so we can mangle it
[ -n "$1" ] || return
IP6_ADDR="$1"
# handle subnet mask for any address containing a /
case "$IP6_ADDR" in
*"/"* ) # set $IP6_GROUP to the number of bits (the characters after the last /)
IP6_GROUP="${IP6_ADDR##*"/"}"
# return failure unless $IP6_GROUP is a positive integer less than or equal to 128
is_numeric "$IP6_GROUP" && [ "$IP6_GROUP" -le 128 ] || return
# remove the subnet mask from the address
IP6_ADDR="${IP6_ADDR%"/$IP6_GROUP"}";;
esac
# perform some preliminary tests and check for the presence of ::
case "$IP6_ADDR" in
# failure cases
# *"::"*"::"* matches multiple occurrences of ::
# *":::"* matches three or more consecutive occurrences of :
# *[^:]":" matches trailing single :
# *"."*":"* matches : after .
*"::"*"::"* | *":::"* | *[^:]":" | *"."*":"* ) return 1;;
*"::"* ) # set flag $IP6_EXPANDED to true, to allow for a variable number of digit groups
IP6_EXPANDED=0
# because :: should not be used for remove a single zero group we start the group count at 1 when :: exists
# NOTE This is a strict interpretation of the standard, applications should not generate such IP addresses but (I think)
# they are in fact technically valid. To allow addresses with single zero groups replaced by :: set $IP6_COUNT to
# zero after this case statement instead
IP6_COUNT=1;;
* ) # set flag $IP6_EXPANDED to false, to forbid a variable number of digit groups
IP6_EXPANDED=""
# initialize count
IP6_COUNT=0;;
esac
# backup current $IFS, set $IFS to : to delimit digit groups
IP6_IFS="$IFS"; IFS=":"
# loop over digit groups
for IP6_GROUP in $IP6_ADDR ;do
# if this is an empty group then increment count and process next group
[ -z "$IP6_GROUP" ] && IP6_COUNT=$(( IP6_COUNT + 1 )) && continue
# handle dotted quad notation groups
case "$IP6_GROUP" in
*"."* ) # return failure if group is not a valid IPv4 address
# NOTE a subnet mask is added to the group to ensure we are matching addresses only, not ranges
! is_ip4 "$IP6_GROUP/1" && IFS="$IP6_IFS" && return 1
# a dotted quad refers to 32 bits, the same as two 16 bit digit groups, so we increment the count by 2
IP6_COUNT=$(( IP6_COUNT + 2 ))
# we can stop processing groups now as we can be certain this is the last group, : after . was caught as a failure case earlier
break;;
esac
# if there are more than 4 characters or any character is not a hex digit then return failure
[ "${#IP6_GROUP}" -gt 4 ] || ! is_hex "$IP6_GROUP" && IFS="$IP6_IFS" && return 1
# increment count
IP6_COUNT=$(( IP6_COUNT + 1 ))
# the following line will prevent the loop continuing to run for invalid addresses with many occurrences of a single :
# this makes no difference to the result, but may improve performance when validating many such invalid strings
[ "$IP6_COUNT" -le 8 ] || break
done
# restore $IFS
IFS="$IP6_IFS"
# if this address contained a :: and it has less than or equal to 8 groups then return success
[ "$IP6_EXPANDED" = "0" ] && [ "$IP6_COUNT" -le 8 ] && return
# if this address contained exactly 8 groups then return success, otherwise return failure
[ "$IP6_COUNT" -eq 8 ]
}
Here are some tests.
# tests
TEST_PASSES=0
TEST_FAILURES=0
for TEST_IP in 0.0.0.0 255.255.255.255 1.2.3.4/1 1.2.3.4/32 12.12.12.12 123.123.123.123 101.201.201.109 ;do
! is_ip4 "$TEST_IP" && printf "IP4 test failed, test case '%s' returned invalid\n" "$TEST_IP" && TEST_FAILURES=$(( TEST_FAILURES + 1 )) || TEST_PASSES=$(( TEST_PASSES + 1 ))
done
for TEST_IP in ::1 ::1/128 ::1/0 ::1234 ::bad ::12 1:2:3:4:5:6:7:8 1234:5678:90ab:cdef:1234:5678:90ab:cdef \
1234:5678:90ab:cdef:1234:5678:90ab:cdef/127 1234:5678:90ab::5678:90ab:cdef/64 f:1234:c:ba:240::1 \
1:2:3:4:5:6:1.2.3.4 ::1.2.3.4 ::1.2.3.4/0 ::ffff:1.2.3.4 ;do
! is_ip6 "$TEST_IP" && printf "IP6 test failed, test case '%s' returned invalid\n" "$TEST_IP" && TEST_FAILURES=$(( TEST_FAILURES + 1 )) || TEST_PASSES=$(( TEST_PASSES + 1 ))
done
for TEST_IP in junk . / 0 -1.0.0.0 1.2.c.0 a.0.0.0 " 1.2.3.4" "1.2.3.4 " " " 01.0.0.0 09.0.0.0 0.0.0.01 \
0.0.0.09 0.09.0.0.0 0.01.0.0 0.0.01.0 0.0.0.a 0.0.0 .0.0.0.0 256.0.0.0 0.0.0.256 "" 0 1 12 \
123 1.2.3.4/s 1.2.3.4/33 1.2.3.4/1/1 ;do
is_ip4 "$TEST_IP" && printf "IP4 test failed, test case '%s' returned valid\n" "$TEST_IP" && TEST_FAILURES=$(( TEST_FAILURES + 1 )) || TEST_PASSES=$(( TEST_PASSES + 1 ))
done
for TEST_IP in junk "" : / :1 ::1/ ::1/1/1 :::1 ::1/129 ::12345 ::bog ::1234:345.234.0.0 ::sdf.d ::1g2 \
1:2:3:44444:5:6:7:8 1:2:3:4:5:6:7 1:2:3:4:5:6:7:8/1c1 1234:5678:90ab:cdef:1234:5678:90ab:cdef:1234/64 \
1234:5678:90ab:cdef:1234:5678::cdef/64 ::1.2.3.4:1 1.2.3.4:: ::1.2.3.4j ::1.2.3.4/ ::1.2.3.4:junk ::1.2.3.4.junk ;do
is_ip6 "$TEST_IP" && printf "IP6 test failed, test case '%s' returned valid\n" "$TEST_IP" && TEST_FAILURES=$(( TEST_FAILURES + 1 )) || TEST_PASSES=$(( TEST_PASSES + 1 ))
done
printf "test complete, %s passes and %s failures\n" "$TEST_PASSES" "$TEST_FAILURES"
Most distros come with package iproute2 (name may vary) preinstalled. So you can rely on the command ip for querying the routing table:
ip -6 route get <probe_addr>/128 >/dev/null 2>&1
Even on a machine without appropriate route this delivers rc=0 when the probe is in valid v6-syntax.
valid_ip(){
ip -6 route get "$1"/128 >/dev/null 2>&1
case "$?" in
0|2) return 0
1) return 1
esac
}
i took sgundlach's answer, but needed it on a machine which did not have ip6 connectivity so through testing and reading the manpage I found that I can trust exit code 1 to mean the syntax is invalid, meanwhile 0 is success and 2 is valid syntax but kernel error reported.
from the man page:
Exit status is 0 if command was successful, and 1 if there is a syntax error. If an error was reported by the kernel exit status is 2.

Resources