How can I use same tasks yum with CentOS and dnf with Fedora on Ansible?

I use CentOS 7 on work and Fedora 28 on hobby, and I’m making an ansible’s playbook that installs some pakcage for them.

But CentOS uses yum and Fedora uses dnf. I know there exists yum module and dnf module, but separeted.

I’d like to write simple, how can I solve it? Could you please tell me.

Solution:

You could use the package module. It’ll sort out dnf vs yum behind the scenes. https://docs.ansible.com/ansible/latest/modules/package_module.html#package-module

Advertisements

Run script command on parallel

i’ve bash script which I need to run on it two command in parallel

For example I’m executing a command of npm install which takes some time (20 -50 secs)

and I run it on two different folders in sequence first npm install on books folder and the second
is for orders folder, is there a way to run both in parallel in shell script ?

For example assume the script is like following:

#!/usr/bin/env bash

   dir=$(pwd)

  cd $tmpDir/books/  

  npm install

  grunt

  npm prune production 
  cd $tmpDir/orders/

  npm install

  grunt

 npm prune production 

Solution:

You could use & to run the process in the background, for example:

#!/bin/sh

cd $HOME/project/books/
npm install &

cd $HOME/project/orders/
npm install &

# if want to wait for the processes to finish
wait

To run and wait for nested/multiple processes you could use a subshell () for example:

#!/bin/sh

(sleep 10 && echo 10 && sleep 1 && echo 1) &

cd $HOME/project/books/
(npm install && grunt && npm prune production ) &

cd $HOME/project/orders/
(npm install && grunt && npm prune production ) &

# waiting ...
wait

In this case, notice the that the commands are within () and using && that means that only the right side will be evaluated if the left size succeeds (exit 0) so for the example:

(sleep 10 && echo 10 && sleep 1 && echo 1) &
  • It creates a subshell putting things between ()
  • runs sleep 10 and if succeeds && then runs echo 10, if succeeds && then run sleep 1 and if succeeds && then runs echo 1
  • run all this in the background by ending the command with &

Resolve variable from config-file based on output

I have a shell script that consist of two files, one bash-file (main.sh) and one file holding all my config-variables(vars.config).

vars.config

domains=("something.com" "else.something.com")

something_com_key="key-to-something"
else_something_com_key="key-to-something else"

In my code i want to loop through the domains array and get the key for the domain.

#!/usr/bin/env sh
source ./vars.config
key="_key"
for i in ${domains[@]}; 
do
    base="$(echo $i | tr . _)" # this swaps out . to _ to match the vars
    let farmid=$base$key 
    echo $farmid
done

So when i run it i get an error message

./main.sh: line 13: let: key-to-something: syntax error: operand
expected (error token is “key-to-something”)

So it actually swaps it out, but i cant save it to a variable.

Solution:

You can expand a variable to the value of its value using ${!var_name}, for example in your code you can do:

key="_key"
for i in ${domains[@]};
do
    base="$(echo $i | tr . _)" # this swaps out . to _ to match the vars
    farmid=$base$key
    farmvalue=${!farmid}
    echo $farmvalue
done

Command working in terminal, but "no closing quote" error when used Process.exec

I have the following method inside a Java program

public static void enableAirplaneMode() {
    String s = null;
    try {
        Runtime rt = Runtime.getRuntime();
        Process pr = null;
        String command = "adb shell \"su -c 'settings put global airplane_mode_on 1'\"";
        System.out.println(command);
        pr = rt.exec(command);

        BufferedReader stdInput = new BufferedReader(new InputStreamReader(pr.getInputStream()));

        while ((s = stdInput.readLine()) != null) {
            System.out.println(s);
        }

        pr.destroy();
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

The output of the above method is

adb shell "su -c 'settings put global airplane_mode_on 1'"
/system/bin/sh: no closing quote

But if I copy/paste the command in terminal directly everything works as expected. Why am I getting the “no closing quote” error here?

Solution:

try passing an array as suggested on javadocs.

String[] command = {"adb", "shell", "su -c 'settings put global airplane_mode_on 1'"};

Get multiple values in an xml file

        <!-- someotherline -->
<add name="core" connectionString="user id=value1;password=value2;Data Source=datasource1.comapany.com;Database=databasename_compny" />

I need to grab the values in userid , password, source, database. Not all lines are in the same format.My desired result would be (username=value1,password=value2, DataSource=datasource1.comapany.com,Database=databasename_compny)

This regex seems little bit more complicated as it is more complicated. Please, explain your answer if possible.

I realised its better to loop through each line. Code I wrote so far

while read p || [[ -n $p ]]; do
  #echo $p
  if [[ $p =~ .*connectionString.* ]]; then
    echo $p
  fi
done <a.config

Now inside the if I have to grab the values.

Solution:

For this solution I am considering:

  • Some lines can contain no data
  • No semi-colon ; is inside the data itself (nor field names)
  • No equal sign = is inside the data itself (nor field names)

A possible solution for you problem would be:

#!/bin/bash

while read p || [[ -n $p ]]; do

  # 1. Only keep what is between the quotes after connectionString=
  filteredLine=`echo $p | sed -n -e 's/^.*connectionString="\(.\+\)".*$/\1/p'`;

  # 2. Ignore empty lines (that do not contain the expected data)
  if [ -z "$filteredLine" ]; then
    continue;
  fi;

  # 3. split each field on a line
  oneFieldByLine=`echo $filteredLine | sed -e 's/;/\r\n/g'`;

  # 4. For each field
  while IFS= read -r field; do

    # extract field name + field value
    fieldName=`echo $field | sed 's/=.*$//'`;
    fieldValue=`echo $field | sed 's/^[^=]*=//' | sed 's/[\r\n]//'`;

    # do stuff with it
    echo "'$fieldName' => '$fieldValue'";

  done < <(printf '%s\n' "$oneFieldByLine")

done <a.xml

Explanations

General sed replacement syntax :

  • sed 's/a/b/' will replace what matches the regex a by the content of b
  • Step 1

    • -n argument tells sed not to output if no match is found. In this case this is useful to ignore useless lines.
    • ^.* – anything at the beginning of the line
    • connectionString=" – literally connectionString=”
    • \(.\+\)" – capturing group to store anything in before the closing quote
    • .*$" – anything until the end of the line
    • \1 tells sed to replace the whole match with only the capturing group (which contains only the data between the quotes)
    • p tells sed to print out the replacement
  • Step 3

    • Replace ; by \r\n ; it is equivalent to splitting by semi-colon because bash can loop over line breaks
  • Step 4 – field name

    • Replaces literal = and the rest of the line with nothing (it removes it)
  • Step 4 – field value

    • Replaces all the characters at the beginning that are not = ([^=] matches all but what is after the ‘^’ symbol) until the equal symbol by nothing.
    • Another sed command removes the line breaks by replacing it with nothing.

Safely remembering ssh credentials in bash script

Imagine I have a bash script that executes commands on a remote machine via ssh:

# Do something here
ssh otheruser@host command1
# Do something else
ssh otheruser@host command2
# Do most local tasks

This script prompts me to enter credentials for otheruser@host multiple times. Is there a safe, easy, and accepted way to cache these credentials for the lifetime of the script but guarantee that they are lost after the script ends (either normally or when an error occurs)? Maybe a solution will use ssh-agent?

I am looking for something like this:

special_credential_saving_command_here # This will prompt for credentials
ssh otheruser@host command1 # This will not prompt now
ssh otheruser@host command2 # This will not prompt either

My motivation here is to avoid entering the credentials multiple times in the same script while not running the risk of those credentials persisting after the script has terminated. Not only is entering the credentials cumbersome, it also requires I wait around for the script to finish so that I can enter the credentials rather than leave it to run on its own (it’s a long running script).

Solution:

Use a control socket to share an authenticated connection among multiple processes:

ssh -fNM -S ~/.ssh/sock otheruser@host  # Will prompt for password, then exit
...
ssh -S ~/.ssh/sock otheruser@host command1
ssh -S ~/.ssh/sock otheruser@host command2
...
ssh -S ~/.ssh/sock -O exit otheruser@host  # Close the master connection

See man ssh_config, under the ControlPath option, for information on how to create a unique path for the control socket.

Getting specific Nth words from variable

I have this script

#!/bin/bash

tmpvar="$*"
doit () {
    echo " ${tmpvar[1]} will be installed "
    apt-get install ${tmpvar[2*]}
    echo " ${tmpvar[1]} was installed "
}
doit

Which works under the command ./file.sh word1 word2 word3 word4
The point is to get the first word for the ‘echos’ and the rest for the installation command.

Example: ./file.sh App app app-gtk
Therefor displaying the first word in both ‘echos’ and getting the rest for the apt command.
But this is not working.

Solution:

You may use shift here:

doit () {
   arg1="$1"  # take first word into a var arg1
   shift      # remove first word from $@

   echo "$arg1 will be installed..."
   # attempt to call apt-get
   if apt-get install "$@"; then
      echo "$arg1 was installed"
   else
      echo "$arg1 couldn't be installed">&2
}

and call this function as:

doit "$@"