How can I do something like
set variable (some_command_that_may_return_a_string)
if [ variable is set ]
do_something
and inversely how do I check if the variable is empty?
set -q var (note the missing "$" - this uses the variable name) can be used to check if a variable has been set.
set -q var[1] can be used to check whether the first element of a variable has been assigned (i.e. whether it is non-empty as a list).
test -n "$var" [fn0] (or [ -n "$var" ]) can be used to check whether a variable expands to a non-empty string (and test -z is the inverse - true if it is empty).
These will be true/false in slightly different circumstances.
When no set var has been performed at all (and it has not been inherited from the parent process), set -q var, set -q var[1] and test -n "$var" will be false, test -z "$var" will be true.
When something like set var has been done (without any additional arguments), set -q var will be true, set -q var[1] will be false.
When something like set var "" has been done, both set versions will be true.
When something like set var "somestring" (or even set var "" "" [fn1]) has been done, the sets will be true and test -z "$var" will be false.
[fn0]: You never want to use test (or [) without quoting the variable. One particularly egregious example is that test -n $var will return true both if the variable contains something and if it is list-empty/unset (no set at all or set var without arguments). This is because fish's test is one of the few parts that follow POSIX, and that demands that test with any one argument be true. Also it does not handle lists properly - test -n $var will have weird results if var has more than one element.
[fn1]: This is because a list will be expanded as a string by joining the elements with spaces, so the list consisting of two empty strings will expand to " " - one space. Since that isn't empty, test -z returns false.
#faho provided an awesome answer but I thought it would be easier if there's a code example.
function if-test-no-arg
set var
if set -q var
# true
end
if set -q var[1]
# false
end
# Always put quotation marks
if test -n "$var"
# false
end
if test -z "$var"
# true
end
end
function if-test-empty-arg
set var ""
if set -q var
# true
end
if set -q var[1]
# true
end
if test -n "$var"
# false
end
if test -z "$var"
# true
end
end
function if-test-valid-arg
set var "hello"
if set -q var
# true
end
if set -q var[1]
# true
end
if test -n "$var"
# true
end
if test -z "$var"
# false
end
end
function if-test-no-set
if set -q var
# false
end
if set -q var[1]
# false
end
if test -n "$var"
# false
end
if test -z "$var"
# true
end
end
The modern version of fish shell (3.5.1) has string commands.
string length --quiet $var checks that the variable is not empty
not string length --quiet $var checks that the variable is empty
Related
How do I test for the presence of a substring in fish shell? For instance, within a switch expression:
set myvar "a long test string"
switch $myvar
case magical-operator-here "test string"
echo 'yep!'
case '*'
echo 'nope!'
end
The * is the wildcard character, so
set myvar "a long test string"
switch $myvar
case "*test string"
echo 'yep!'
case '*'
echo 'nope!'
end
If you wish to test if it ends with that string. If it can also appear somewhere in the middle, add another * at the end.
Also, since 2.3.0, fish has had a string builtin with a match subcommand, so you could also use string match -q -- "*test string" $myvar. It also supports pcre-style regexes with the "-r" option.
I am trying to write a script that basically executes a cli command like:
snmpget -v 1 -c xxxxxx-Ovq xx.xx.xx.xxx .1.3.6.1.2.1.1.8.0
where xxxxx is a password and xx.xx.xx.xxx and IP that normally returns:
49:22:12:15.00
My script is:
#!/usr/local/bin/perl
#snmpget -v 1 -c xxxxx -Ovq xx.xx.xx.xxx .1.3.6.1.2.1.1.8.0
$SNMP_GET_CMD = "snmpget -v1 -c xxxxx-Ovq";
$SNMP_TARGET = "xx.xx.xx.xxx";
my $sysORLastChange = '${SNMP_GET_CMD} ${SNMP_TARGET} .1.3.6.1.2.1.1.8.0';
chomp($sysORLastChange);
print("${SNMP_TARGET} as an Input Line Reading of ${sysORLastChange}\n");
and the output is:
xx.xx.xx.xxx as an Input Line Reading of ${SNMP_GET_CMD} ${SNMP_TARGET} .1.3.6.1.2.1.1.8.0
It should return the following:
xx.xx.xx.xxx as an Input Line Reading of 49:22:12:15.00
Is there any problem with the syntax i used in the script?
In Perl, use double-quotes to interpolate another variable into a string. When you define $sysORLastChange using other variables within a single-quoted string like this:
my $sysORLastChange = '${SNMP_GET_CMD} ${SNMP_TARGET} .1.3.6.1.2.1.1.8.0';
...the string is being assigned verbatim (ie. the inner variables aren't being expanded).
To correct this, assign to the variable using double-quotes, which will interpolate the inner variables into their values:
my $sysORLastChange = "${SNMP_GET_CMD} ${SNMP_TARGET} .1.3.6.1.2.1.1.8.0";
If you want to actually execute the string, you can use the qx() operator, aka the "backtick" style quotes:
my $sysORLastChange = qx(${SNMP_GET_CMD} ${SNMP_TARGET} .1.3.6.1.2.1.1.8.0);
# or...
my $sysORLastChange = `${SNMP_GET_CMD} ${SNMP_TARGET} .1.3.6.1.2.1.1.8.0`;
See Perl Quote and Quote-like Operators in perlop.
I'm trying to use sed to replace a specific line within a configuration file:
The pattern for the line I want to replace is:
ALLOWED_HOSTS.*
The text I want to insert is:
'$PublicIP' (Including the single ticks)
But when I run the command:
sed 's/ALLOWED_HOSTS.*/ALLOWED_HOSTS = ['$PublicIP']/g' /root/project/django/mysite/mysite/settings.py
The line is changed to:
ALLOWED_HOSTS = [1.1.1.1]
instead of:
ALLOWED_HOSTS = ['1.1.1.1']
How shall I edit the command to include the single ticks as well?
You could try to escape the single ticks , or better you can reassign the variable including the simple ticks:
PublicIP="'$PublicIP'".
By the way even this sed without redifining var, works ok in my case:
$ a="3.3.3.3"
$ echo "ALLOWED_HOSTS = [2.2.2.2]" |sed 's/2.2.2.2/'"'$a'"'/g'
ALLOWED_HOSTS = ['3.3.3.3']
Even this works ok:
$ echo "ALLOWED_HOSTS = [2.2.2.2]" |sed "s/2.2.2.2/'$a'/g"
ALLOWED_HOSTS = ['3.3.3.3']
Given this program:
#!/bin/env perl6
sub MAIN ($filename='test.fq', :$seed=floor(now) )
{
say "Seed is $seed";
}
When I run it without any command line arguments, it works fine. However, when I give it a command line argument for seed, it says that its value is True:
./seed.p6 --seed 1234
Seed is True
Why is the number 1234 being interpreted as a boolean?
Perl 6's MAIN argument handling plays well with gradual typing. Arguments can, and should be typecast to reduce ambiguity and improve validation:
#!/bin/env perl6
sub MAIN (Str $filename='test.fq', Int :$seed=floor(now))
{
say "Seed is $seed.";
}
After typecasting seed to Int, this option must be given a numeric argument and no longer defaults to a Boolean:
perl6 ./seed.pl -seed 1234
Usage:
./seed.pl [--seed=<Int>] [<filename>]
perl6 ./seed.pl -seed=abc
Usage:
./seed.pl [--seed=<Int>] [<filename>]
perl6 ./seed.pl -seed=1234
Seed is 1234.
You need to use an = sign between your option --seed and its value 1234:
./seed.p6 --seed=1234
Since you have a positional argument in your MAIN subroutine signature (i.e. $filename), the first argument not tied to an value with an = sign will be assigned to it.
Your original
./seed.p6 --seed 1234
was being interpreted as if 1234 were the filename (i.e. it was assigned to the variable $filename). Since a command line option without an argument is considered to be True, $seed was being assigned True in your original invocation of that script.
contents of expect_out(buffer)
GigabitEthernet1/0/9 unassigned YES unset up up
GigabitEthernet1/0/10 unassigned YES unset down down
GigabitEthernet1/0/11 unassigned YES unset down down
GigabitEthernet1/0/23 unassigned YES unset down down
GigabitEthernet1/0/24 unassigned YES unset down down
GigabitEthernet1/1/1 unassigned YES unset down down
GigabitEthernet1/1/2 unassigned YES unset down down
GigabitEthernet1/1/3 unassigned YES unset down down
GigabitEthernet1/1/4 unassigned YES unset down down
Te1/1/1 unassigned YES unset down down
Te1/1/2 unassigned YES unset down down
FastEthernet2/0/1 unassigned YES unset down down
FastEthernet2/0/2 unassigned YES unset down down
FastEthernet2/0/24 unassigned YES unset down down
GigabitEthernet2/0/1 unassigned YES unset up up
GigabitEthernet2/0/2 unassigned YES unset down down
I have the foloowing data above and i need to count the number of data for each type
so that i can have the info like :
GigabitEthernet1 : 20
GigabitEthernet2 : 20
Tel : 2
FastEthernet2 : 4
FastEthernet1 : 4
total : 50
How can I do it?
Any help would be appreciated because I don't know in which direction to proceed because I am a novice as far as expect/tcl is concerned.
I tried to use split function to parse it by using newline as delimiter so that I can use regex inside for loop but it seems that because $expect_output(buffer) is a variable it might not have any lines in it.
Moreover can I use awk or sed inside expect then it would be not so difficult I guess. But an expected solution would be standard.
based on your current input data, this one-liner:
awk -F'/' '{a[$1]++}END{for(x in a){print x" : "a[x];t+=a[x];}print "total : "t}' file
gives:
FastEthernet2 : 3
GigabitEthernet1 : 9
GigabitEthernet2 : 2
Te1 : 2
total : 16
Since Expect is based on Tcl/TK you should familiarize yourself with that language since it contains numerous string handling options. Here is some code which hopefully sets you on the right track.
set str $expect_out(buffer)
# Strip everything after slash
regsub -all -line "/.*" $str "" str2
puts $str2 # just to see what you got so far
# Convert string into list
set li [split $str2 "\n"]
# Convert list into array
# This is actually the tricky part which converts the list into an
# associative array whose entries have first to be set to one
# and later have to be increased by one
for {set i 0} {$i < [llength $li]} {incr i} {
if { [info exists arr([lindex $li $i]) ] } {
incr arr([lindex $li $i]) } {
set arr([lindex $li $i]) 1 }
}
# Now get the statistics
array get arr
# will print this for your example
# GigabitEthernet2 2 Te1 2 FastEthernet2 3 GigabitEthernet1 9
And you should tag this question with Tcl and TK too.