Puppet exec unless doesn't appear to work how I expect - puppet

I'm trying to clone a db with exec from mysql, but I don't want to clone it if it has already been cloned.
exec { "clone_from_${name}" :
unless => "/usr/bin/mysql -u${user_to} -p${pwd_to} ${name_to} -e'select count(*) from $test_table_name;' | grep -c ' 0 '",
...
The logic looks a little confusing but basically, the way I understand unless is that if the command returns a 0, then the whole exec wont be run. But it when I try it, it is.
The db has already been created in the vm, and if it's already been cloned, the count returned from the query gives me something other than a ' 0 ', and the grep because it doesn't find the ' 0 ' returns a 0. Unless should make it run then, right?
The output even gives me "Unless: 0"
and then "Executing
Thanks.

It's the exitcode that is important, not the number that is printed.
If you run this command manually and do an echo $? afterwards, you get the exitcode.
Also, you want to surpress the output from the command:
exec { "clone_from_${name}" :
unless => "/usr/bin/mysql -u${user_to} -p${pwd_to} ${name_to} -e'select count(*) from $test_table_name;' | grep ' 0 ' > /dev/null 2>&1",
...

Related

Coloring a variable in "read -p" command

I would like to color a variable in the "read -p" command. In another topic i found
a way to color the text string like this:
read -p $'\e[31mFoobar\e[0m: ' <= works
But if I put a variable instead of 'Foobar' the value is not shown because the single quotes are preventing the call of the variable $mmd.
read -p $'\e[31m $mmd \e[0m: ' <= doesnt ork
Do you know a way to make this work?
I tried:
read -p $'\e[31m $mmd \e[0m: ' <= doesnt work
read -p $"\e[31m $mmd \e[0m: " <= doesnt work
You can terminate and restart the escape sequence, thus moving the variable expansion outside, as per the following transcript:
pax:~> mmd="Enter something"
pax:~> read -p $'\e[31m'"${mmd}"$'\e[0m: '
Enter something: hello<enter>
pax:~> echo ${REPLY}
hello

Assigning one variable to another in Bash

ip="192.168.1.1"
if [ -n "$(ip = 192.168.1.1)" ];
then
IPADDR=$(ip addr show |grep 'inet '|grep -v 127.0.0.1 |awk '{print $2}'| cut -d/ -f1)
else
"${ip}"="${IPADDR}"
fi
echo "${IPADDR}"
Im trying to assigning ip="192.168.1.1" to variable IPADDR
the error im geting atm is
Object "=" is unknown, try "ip help".
./test: line 7: 192.168.1.1=: command not found
Here ...
if [ -n "$(ip = 192.168.1.1)" ];
... you are executing the command ip, passing arguments = and 192.168.1.1, and capturing its standard output. These are not valid arguments for that command, so ip emits an error message to its standard error, but nothing to its standard output.
The test [ -n "$(ip = 192.168.1.1)" ] evaluates whether the captured output is non-empty, so this test fails, and consequently, the else block is executed. Here ...
"${ip}"="${IPADDR}"
... is not a variable assignment but rather a command, because the text to the left of the = is not (just) an identifier. The IPADDR variable is unset or null, so that expands to 192.168.1.1=. The shell reports that there is no command of that name.
It's hard to be sure what you were really trying to accomplish, but going with the question title, if you want to assign a value to a variable then the syntax is
variable_name=value
The variable_name must be an identifier. The value is subject to expansion and quote removal, but not word splitting, so either of these would work for what you appear to be trying to do
ip="${IPADDR}"
or
ip=${IPADDR}
. With that said, the code does not make sense, because it does not assign a value to variable IPADDR before attempting that assignment.
Earlier, it is possible that you meant
if [ "$ip" != 192.168.1.1 ]
... which tests whether the value of variable $ip, considered as a string, is equal to the string 192.168.1.1. Even then, however, the code does not make sense overall.

shell script has been retrieving from edbplus sql results with echo outputs

I am trying to call edbplus to count a table from a command-line linux shell script, but I have been retrieving from edbplus the response number with others outputs in the same response, I am trying to retrieve from it only an integer response number.
#!/bin/sh
COUNT=`./edbplus.sh -silent user/password#localhost:5444/mydb<<-EOF
SET PAGESIZE 0 FEEDBACK OFF VERIFY OFF HEADING OFF ECHO OFF
SELECT COUNT(ID) FROM MYTABLE
EXIT;
EOF`
echo $COUNT
Response:
$ echo $COUNT
6-------------------d always takes 2 parameters: variable_name value
Do you know how get only the integer number?
If the 1st value is going to be integer. Please try the below commands
echo $COUNT | cut -d - -f 1
(or)
if only one int value if required, then please try
echo $COUNT | cut -c 1
To solve it from EDB perspective:
If the below flags are used in EDB in single line, then the above issue would have caused.
SET PAGESIZE 0
SET FEEDBACK OFF
SET VERIFY OFF
SET HEADING OFF
SET ECHO OFF
Kindly update it as above and provide it in individual lines.

get full current crontab line, and create unique result email headers (from, subject, etc) per line

I have a giant crontab with many scripts on a linux machine. I need to be able to a) change the subject and/or from of cronjob result emails, because the default is unreadably long. b) Do so via a centralized solution. c) Only require minimal changes to the crontab itself.
For example this crontab line:
0 */3 * * * /path/to/script1 | /path/to/script2 | /path/to/script3
Creates this email subject:
Cron <cronuser#myserver> /path/to/script1 | /path/to/script2 | /path/to/script3
Which in my inbox gets cut off somewhere in the path to script1. (And many lines in the crontab are significantly longer.)
Options I've tried:
Piping to mail and setting subject etc per line (The -E preserves cron's default only-send-on-output behavior):
0 */3 * * * /path/to/script1 | /path/to/script2 | /path/to/script3 2>&1 | mail -E -s "test subject" -S from="Cron Script2 <cronuser#myserver.com>" recipient#myserver.com
This "works", but I want to centralize my changes in one place, and minimize how much I add to each cron line for readability
Using shell : (noop) command, which shows up first in the subject (note that the space after the : is important!):
0 */3 * * * : Descriptive Words; /path/to/script1 | /path/to/script2 | /path/to/script3
Unfortunately there's still too much unnecessary that cron puts before "Descriptive Words" on the subject line, so this is still unusable.
What I want to create:
Something generic, like this:
0 */3 * * * /path/to/script1 | /path/to/script2 | /path/to/script3 2>&1 | coolmailer.pl
coolmailer.pl would build the subject line by getting the commands on that cron line, strip out the paths and arguments, and email me this (optionally only if there's a fail in any of the scripts):
SUBJECT: script1 | script2 | script3
FROM: Cron Script3<noreply#myserver.com>
(actual results of the command /path/to/script1 | /path/to/script2 | /path/to/script3)
As a bonus, I'd also love to say whether any of the previous commands (script1 or script2) failed on the subject line.
This has turned out to be... way more complicated than I expected.
Challenges:
Find a way for a pipeline member (coolmailer) to know the other
members of the pipeline.
There's an ingenious method for 1 here using lsof how-do-you-determine-the-actual-command-that-is-piping-into-you but it also sometimes finds commands started by the scripts in my pipeline (ie if script1 forks processes or does system calls, those show up too any time they take long enough to complete.) Ditto for the method using process groups at the same link.
Find a way for a pipeline member (coolmailer) to know the results of
other members of the pipeline. (I realize this may not be possible at all, but the lsof hack gives me hope.)
Any better way? Does the fact that I'm running from cron buy me anything? Part of me wants to combine the lsof strategy with grepping through crontab -l results, but that just seems too kludgy and prone to errors.
Caveats:
I can have changes made to my account, but I can't make changes that
would effect all users. I.e. if there's a way to change cron's
mailing format server-wide, that doesn't help.
I can't realistically update every script called to handle emailing its own results, even if that's probably the "right" way.
I know about the mail -s -E -S options, but would prefer to have a single place to change things. Also, I really want to find a way to get the pipeline.
Language used now for "coolmailer" is Perl, but I'll try anything
My first attempt:
(which works, except it often also shows other commands started inside my scripts, which means it doesn't work)
#!/usr/bin/perl -w
my $pgid=`ps -o pgid= -p $$`;
my $lsofout = `/usr/sbin/lsof -g $pgid`;
my #otherpids = `echo "$lsofout" | awk '\$5 == "1w" { print \$2 }'`;
my #longcmds;
my #shortcmds;
foreach my $pid (#otherpids) {
chomp($pid);
if (my $cmd = `ps -o cmd= -p $pid 2>/dev/null`) {
chomp($cmd);
push #longcmds, $cmd;
next;
}
}
my $cmdline = join (' | ',#longcmds);
foreach my $cmd (#longcmds) {
$cmd =~ s/(\/\S+\/)(\S+)/$2/g;
push #shortcmds, $cmd;
}
my $subj = join('|',#shortcmds);
print "SUBJ:$subj\n";
print "CMDLINE: $cmdline\n";
# and now do some mail stuff
And final version, based on suggestion by Jhnc
#!/usr/bin/perl -w
# cronmgr.pl -- understand cron emails for once
# usage: 0 */3 * * * cronmgr.pl cd blah\; /path/to/script1 \| /path/to/script2 \| /path/to/script3
# note that ; | & in any cronmgr.pl line must be backslashed to run!
use strict;
use IPC::Cmd qw[can_run run run_forked];
my $CMDLINE = join(' ',#ARGV);
my( $success, $error_message, $full_buf, $stdout_buf, $stderr_buf ) =
run( command => $CMDLINE, verbose => 0 ); # verbose = 0 means don't output normally, capture all output
my ($stdout, $stderr);
$stdout = join "", #$stdout_buf;
$stderr = join "", #$stderr_buf;
my $emailsubject;
if( $success ) {
if ($stdout eq '' && $stderr eq '') { # if there's no output, don't send any email!
exit;
}
} else {
print "CMD FAIL!\n$error_message\nSTDERR:\n$stderr";
$emailsubject = "FAIL:$error_message";
}
# etc etc
(Edited for clarity re goals and why options attempted so far aren't sufficient.)
determining the pipeline
If you always invoke coolmailer.pl with a unique argument then you can simply grep it from your list of cronjobs:
#!/usr/bin/perl -wT
$ENV{PATH} = '/sensible:/path';
my ($pipeline) = grep /\|\s+$0\s+$ARGV[0]/, `crontab -l`;
$pipeline ||= "oops";
# ... mung $pipeline ...
# ... do mail stuff ...
checking pipe failure
If you rewrite your cronjob entries from:
/path/to/script1 | /path/to/script2 | /path/to/script3 2>&1 | coolmailer.pl
to:
coolermailer /path/to/script1 \| /path/to/script2 \| /path/to/script3
then you could construct the pipeline manually and have control over pipe member status information. (This also gives you the pipeline directly, although you then have to construct it before it will run.)
For example, with a bash implementation, you might make use of eval and PIPESTATUS. With Perl, you might use results() from IPC::Run

Show job count in bash prompt only if nonzero

A typical prompt in bash in something like:
PS1="\u#\h:\w\$ "
The you can show the number of background jobs using \j, e.g.:
PS1="\u#\h:\w [\j]\$ "
Which is useful, because every now and then I forget I have a stopped job and only notice when it complains if I manually logout from the shell.
However, 95% of the time, the background job count is 0 and showing it in the prompt is superfluous.
How can I show the job count in the prompt, but only if it's nonzero?
You can e.g. do something like this:
PS1='\u#\h:\w $([ \j -gt 0 ] && echo [\j])\$ '
The accepted answer does not work for me (I have got Bash v4.2.46). It throws an error like this:
[: \j: integer expression expected
I had to use PROMPT_COMMAND to achieve the same functionality:
export PROMPT_COMMAND=__prompt_command
function __prompt_command() {
local JOBS=$(jobs | wc -l | tr -d 0)
PS1="\u#\h:\w [${JOBS}]\$ "
}

Resources