Pros and Cons of Robotium

Hi guys, I am writing just a summary about Robotium, because maybe I will start to work with it. So, if you have any tips or suggestion, please feel free to comment here.

Robotium is a popular Android automation framework for testing native and hybrid Android apps using the black box method. Licensed under an Apache 2.0 license and first released in 2010.

To use Robotium, you need either the source code or apk file for the app, Eclipse for building a test project, ADT (Android Development Tools), SDK (Software Development Kit), JDK (Java Development Kit), and the Robotium.jar file.

Benefits:
• You can develop powerful test cases, with minimal knowledge of the application under test.
• Allows user to test more flexible and convenient for analyzing results.
• Robotium allows us to take screenshots anywhere in the test (both for Emulator and Device) and save it to device internal memory or SD Card or Emulator
• The framework handles multiple Android activities automatically.
• Minimal time needed to write solid test cases.
• Readability of test cases is greatly improved, compared to standard instrumentation tests.
• Test cases are more robust due to the run-time binding to GUI components.
• Blazing fast test case execution.
• Automatic timing and delays.

Limitations of Robotium:
• Tied to JUnit 3 Instrumentation on device.
• Tied to one app process.
• It can’t work with different Applications in on test – if your application call another one (like Camera) – Robotium can’t “see” it and press any buttons there.

Parallel tests:

I’ve found this API: https://github.com/square/spoon, but I believe that we can configure parallel tests with Jenkins too.

 

Thank you guys ! See you next week 🙂

 

Sources:

https://saucelabs.com/resources/articles/open-source-tools-robotium-android-appium

https://code.google.com/p/robotium/

https://www.linkedin.com/grp/post/3769150-5852687643892002817

http://blog.mobinius.com/robotium-best-testing-framework-for-android/

Third script with Velocity (VM)

Hi guys, I’ve been busy this week, for this reason I am posting today not Wednesday as I am used to post. Ok, So I will finish the examples with vm scripts in this post.

I’ve updated the last script and the template with another stuff that you can do with vm template. So, as you can see, you can write Java code inside the template. Like, if you want a template which it will share some parts of another template, you can put a condition and then you will be reducing the number of the templates.

– You can keep the getters and setters from Customer.Class

– You will need modify CostumerSearch: Add the petlist (New value which we are sending) and create the hashmap with different values

package test;
import org.apache.velocity.Template;
import java.io.StringWriter;
import java.util.ArrayList; 
import java.util.HashMap; 
import java.util.Map;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.VelocityContext;
import test.Customer;

public class CustomerSearch  {
    
    public static void main(String[] args) throws Exception { 

     CustomerSearch cs = new CustomerSearch(); 

     StringWriter writer = cs.getTemplate(); 
     System.out.println(writer.toString());
    }
    public StringWriter getTemplate()  {

        Template template = null;
        StringWriter writer = new StringWriter();
        Customer customer = getCustomer();
        
        ArrayList<Map> list = new ArrayList<Map>(); 
        Map<String, String> map = new HashMap<String, String>(); 
        
        map.put("category", "dog"); 
        map.put("name", "Whisky"); 
        list.add( map ); 

        map = new HashMap<String, String>(); 
        map.put("category", "cat"); 
        map.put("name", "Madonna"); 
        list.add( map ); 

        map = new HashMap<String, String>(); 
        map.put("category", "fish"); 
        map.put("name", "Levels"); 
        list.add( map );
        VelocityContext context = new VelocityContext();
        try  {
            context.put("petList", list);
            context.put("customer", customer);
            template = Velocity.getTemplate("src/customer.vm");
            template.merge(context, writer); 
        }  catch( Exception e )  {
          System.err.println("Exception caught: " + e.getMessage());
        }

        return writer;
    }

    private Customer getCustomer()  {
        Customer customer = new Customer();
        customer.setCustomerNumber("ABC123");
        customer.setName("Joe JavaRanch");
        customer.setAddress("123 Rancho Javo");
        customer.setCity("Bueno Ranch");
        customer.setState("CO");
        customer.setZip("63121");
        customer.setPhone("303-555-1212");
        return customer;
    }
}

– Now you need to modify the template. In this example, I created a for with the hashmap and added a condition to write pets unless fish.

<html> 
  <head>
   <title>Customer Search Results - $customer.customerNumber</title>
  </head>
  <body bgcolor="#faf7f1"> 
  <h1>Customer information for customer number $customer.customerNumber</h1>
  <b>Name:</b> $customer.name<br>
  <b>Address:</b> $customer.address<br>
  <b>City:</b> $customer.city<br>
  <b>State:</b> $customer.state<br>
  <b>Zip:</b> $customer.zip<br>
  <b>Phone:</b> $customer.phone<br>
  
  #foreach( $pet in $petList ) 
   #if ($pet.category != "fish") 
    <b>$pet.category </b>- Name: $pet.name<br> 
   #end 
  #end 

  </body>
</html>

I mixed a lot of things here (Hashmap,singleton, etc.), but now you can write the way you need. I hope something here helps you 🙂

Cheers guys !

Source:

http://www.javaworld.com/article/2075966/core-java/start-up-the-velocity-template-engine.html

How to: Take a screenshot with CodedUI and C#

Hi guys,

Last week I was on holidays, for this reason I haven’t posted anything. So, I will continue with VM scripts and what you can do with them next week. Today, I will post a very, very, very old code that I have done when I was building a framework for a desktop application with Coded UI and C#. It is a function to take screenshots in your automation.

I will refactor this code other time, because I know that it is not following all the best practices, but for now you can copy and change where you want.

The code is on my github:

https://github.com/rafaelaazevedo/CodedUI/blob/master/PrintScreen.cs

First: you can see what are the parameters that you need to send:  (PublicFunctions Values, string _stringMessage, Boolean FULLSCREEN = false, Boolean FAIL = false)

Second: intFail is a failure counter and stringPath is a variable that you need to update with the path where will be placed your screenshot

Third: Thread.Sleep(2000) you can remove this and put something like wait for, it is not the best solution use Thread.Sleep

Fourth: This part will take the FULLSCREEN or not, depending what you have sent

Fifth: This part is just formatting the date/time.

Sixth: Now it is saving the picture with the path and the date/time. It is missing a try/catch here, please don’t forget to put in your code xD

Seventh: It is clearing the clipboard for the next screenshot

Eighth: It is counting the failure and catching the failure message

Ninth: The last step is inserting the failure into the database with all the information that you are passing

Thank you guys ! See you next week 🙂

Second script with Velocity (VM)

Hi guys, Today I will post another example of VM template, sorry for not to post something new, but I’ve got flu this week, I am feeling like a zombie 😦

This example is more complete and I have tested, so it is working 😀 I just made some changes to print the result with the values and the template. We have get and set methods, the vm template in html and the class to set the values and merge the template.

– Create the get and set customer class:

package test;
public class Customer {

    String customerNumber;
    String name;
    String address;
    String city;
    String state;
    String zip;
    String phone;

    public String getCustomerNumber() {return customerNumber;}
    public void setCustomerNumber(String s) {customerNumber = s;}
    public String getName() {return name;}
    public void setName(String s) {name = s;}
    public String getAddress() {return address;}
    public void setAddress(String s) {address = s;}
    public String getCity() {return city;}
    public void setCity(String s) {city = s;}
    public String getState() {return state;}
    public void setState(String s) {state = s;}
    public String getZip() {return zip;}
    public void setZip(String s) {zip = s;}
    public String getPhone() {return phone;}
    public void setPhone(String s) {phone = s;}
}

– Create a customer search class which will set the values and merge with the template:

package test;
import org.apache.velocity.Template;
import java.io.StringWriter;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.VelocityContext;
import test.Customer;

public class CustomerSearch  {
    
    public static void main(String[] args) throws Exception { 

     CustomerSearch cs = new CustomerSearch(); 

     StringWriter writer = cs.getTemplate(); 
     System.out.println(writer.toString());
    }
    public StringWriter getTemplate()  {

        Template template = null;
        StringWriter writer = new StringWriter();
        Customer customer = getCustomer();
        
        VelocityContext context = new VelocityContext();
        try  {
            context.put("customer", customer);
            template = Velocity.getTemplate("src/customer.vm");
            template.merge(context, writer); 
        }  catch( Exception e )  {
          System.err.println("Exception caught: " + e.getMessage());
        }

        return writer;
    }

    private Customer getCustomer()  {
        Customer customer = new Customer();
        customer.setCustomerNumber("ABC123");
        customer.setName("Joe JavaRanch");
        customer.setAddress("123 Rancho Javo");
        customer.setCity("Bueno Ranch");
        customer.setState("CO");
        customer.setZip("63121");
        customer.setPhone("303-555-1212");
        return customer;
    }
}

– After, create the vm template html:

<html>
<head><title>Customer Search Results - $customer.customerNumber</title></head>
<body bgcolor="#faf7f1">
    <h1>Customer information for customer number $customer.customerNumber</h1>
    <b>Name:</b> $customer.name<br>
    <b>Address:</b> $customer.address<br>
    <b>City:</b> $customer.city<br>
    <b>State:</b> $customer.state<br>
    <b>Zip:</b> $customer.zip<br>
    <b>Phone:</b> $customer.phone<br>
</body>
<html>

Cheers guys !

Source:

http://velocity.apache.org/engine/devel/developer-guide.html

https://velocity.apache.org/engine/releases/velocity-1.5/webapps.html

http://www.javaranch.com/journal/2004/03/Velocity-AnIntroduction.html

http://www.javaworld.com/article/2075966/core-java/start-up-the-velocity-template-engine.html

First script with Velocity (VM)

Velocity is a Java-based template engine. It permits web page designers to reference methods defined in Java code. Basically, you can create files (HTML, XML, etc…) writing scripts and generate them following a template.

 

– Create Maven Project.

– Add dependency in your pom file – Change the version of the lib:

<dependency>

  <groupId>org.apache.velocity</groupId>

  <artifactId>velocity</artifactId>

  <version>1.6.4</version>

</dependency>

 


– Create a Vm template, like this one. Save as .vm (I created outside the package test). So, place your template in Project > src > customer.vm and not with the code which the structure is: Project > src > package.test:

Hello $word! This is your first Velocity script !

 

– Create a Java Class called CreateScript:
import java.io.StringWriter;

import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.exception.MethodInvocationException;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;

public class createXml {
   public static void main(String[] args) throws Exception {
   VelocityEngine ve = new VelocityEngine();
   ve.init();

   try {
    Template t = ve.getTemplate("src/helloworld.vm");

    VelocityContext context = new VelocityContext();
    context.put("word", "World");
    StringWriter writer = new StringWriter();
    t.merge(context, writer);
    System.out.println(writer.toString());
    } catch (ResourceNotFoundException rnfe) {
      System.out.println("Couldn't find the template");
    } catch (ParseErrorException pee) {
      System.out.println("syntax error: problem parsing the template");
    } catch (MethodInvocationException mie) {
      System.out.println("something invoked in the template");
      System.out.println("threw an exception");
    } catch (Exception e) {
    }
  }
}

– Vm file is the template, you will create the template and what the variables that will have the value that you want.

– Inside Java Class:

You need initialize the Velocity API – Write the path of the vm template – Initialize the context and write all the variables and the values (name – variable, word – value) – Initialize the Writer which will render the template – Merge the context and the values creating the template – Show the result

– To run the project, first I had to compil with maven test and after this I just choose Run As – Java Application.

So, this is the first script, I will write more about, but between this you can have a look on the links below. See you guys 🙂

 

Sources:

http://velocity.apache.org/engine/devel/developer-guide.html

https://velocity.apache.org/engine/releases/velocity-1.5/webapps.html

http://www.javaranch.com/journal/2004/03/Velocity-AnIntroduction.html

http://www.javaworld.com/article/2075966/core-java/start-up-the-velocity-template-engine.html

Functional Programming

What is functional programming?  Everything is a mathematical function. Functional programming languages can have objects, but generally those objects are immutable — either arguments or return values to functions. There are no for/next loops, as those imply state changes. Instead, that type of looping is performed with recursion and by passing functions as arguments.

Functional programming requires that functions are first-class, which means that they are treated like any other values and can be passed as arguments to other functions or be returned as a result of a function. Being first-class also means that it is possible to define and manipulate functions from within other functions. Special attention needs to be given to functions that reference local variables from their scope. If such a function escapes their block after being returned from it, the local variables must be retained in memory, as they might be needed later when the function is called.

The following table shows which languages support functional programming (by supporting first-class functions) and for which the functional style is the dominant one.

Language Closures Functional
C No No
Pascal No No
C++ Yes No
Java Yes No
Modula-3 Yes No
Python Yes No
Ruby Yes No
Javascript Yes Yes
Ocaml Yes Yes
Erlang Yes Yes
Haskell Yes Yes

Benefits

Functional programming is known to provide better support for structured programming than imperative programming. It is easy, for instance, to abstract out a recurring piece of code by creating a higher-order function, which will make the resulting code more declarative and comprehensible.

Functional programs are often shorter and easier to understand than their imperative counterparts. Since various studies have shown that the average programmer’s productivity in terms of lines of code is more or less the same for any programming language, this translates also to higher productivity.

Look the difference:

Class using C and object-oriented:

public static class SumOfSquaresHelper
{
   public static int Square(int i)
   {
      return i * i;
   }

   public static int SumOfSquares(int n)
   {
      int sum = 0;
      for (int i = 1; i <= n; i++)
      {
         sum += Square(i);
      }
      return sum;
   }
}

Does the same thing using Functional programming:

let square x = x * x
let sumOfSquares n = [1..n] |> List.map square |> List.sum

Object-oriented x Functional languages

When you anticipate a different kind of software evolution:

  • Object-oriented languages are good when you have a fixed set of operations on things, and as your code evolves, you primarily add new things. This can be accomplished by adding new classes which implement existing methods, and the existing classes are left alone.
  • Functional languages are good when you have a fixed set of things, and as your code evolves, you primarily add new operations on existing things. This can be accomplished by adding new functions which compute with existing data types, and the existing functions are left alone.

When evolution goes the wrong way, you have problems:

  • Adding a new operation to an object-oriented program may require editing many class definitions to add a new method.
  • Adding a new kind of thing to a functional program may require editing many function definitions to add a new case.

I have been reading about what is functional programming on the last weeks (Just because I was curious) and I found a lot of articles, but I summarized here a little piece of each place which helped me to better understand what really is.

If you are interested about functional programming you can click on this link. I found it very interesting and easy to understand.

 See you next week !

Source: http://www.javacodegeeks.com/2014/03/functional-programming-with-java-8-lambda-expressions-monads.html

http://www.functionaljava.org/

https://pragprog.com/magazines/2013-01/functional-programming-basics

http://programmers.stackexchange.com/questions/117276/example-of-where-functional-programming-is-superior-to-imperative-or-object-orie

http://c2.com/cgi/wiki?FunctionalProgramming

http://stackoverflow.com/questions/2078978/functional-programming-vs-object-oriented-programming

http://www.infoworld.com/article/2615766/application-development/functional-programming–a-step-backward.html?page=2

http://fsharpforfunandprofit.com/why-use-fsharp/

http://www.tryfsharp.org/Learn

Code example to catch url, Status code and time to load an url

Hi guys, today I will post a simple project that I did just to catch some information with Selenium and Java. So, I have a spreadsheet with some urls in the first column and when I run the project, it is being recorded the status code of the urls and how long is lasting to load this page in the following columns.

It is very simples. I know that there are many ways to do that and you don’t need to use selenium because will take more time to render the page and everything, but maybe someone is needing something similar. So, now the code and in the bottom the link to my project.

  • OPEN URL – Open the URL and write the StatusCode and the time to load the page in the following columns:
import jxl.read.biff.BiffException;
import org.openqa.selenium.WebDriver;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Workbook;
import org.openqa.selenium.By;

public class OpenUrl {

    OpenDataBase opendatabase = new OpenDataBase();
    FileOutputStream fOut = null;

    public void OpenURL(WebDriver driver) throws BiffException, Exception {
        String tablename = ("tb");
        opendatabase.Open(tablename);
        File file = new File("BD.xls");
        int result = 0;
        String STR = "CRI -";
        int ARQ = 1;
        int WARNING = 2;
        int CRITICAL = 3;

        Workbook wb = new HSSFWorkbook(new FileInputStream(file.
getCanonicalPath()));

        for (int i = 0; i < opendatabase.sheet.getRows(); i++) {

            String url = opendatabase.sheet.getCell(0, i).getContents();

            double start = System.currentTimeMillis();
            driver.manage().window().maximize();
            driver.get(url);
            double finish = System.currentTimeMillis();
            double totalTime = finish - start;
            double totalTimeSec = totalTime / 1000;

            try {
                driver.findElement(By.xpath("/html/body/div/span[2]/ul/li/a")
).click();
            } catch (Exception e) {
                driver.findElement(By.xpath("/html/body/div/span/ul/li/a")
).click();
            }

            String titlepage = driver.getTitle().replace(" ", "");

            FileWriter fw = new FileWriter(titlepage + ".txt");
            int statusCode = Statuscode(driver.getCurrentUrl(), driver);
            String line = url + "," + totalTimeSec + "," + statusCode;

            fw.write(line);
            fw.close();


        }


    }

    public int Statuscode(String URLName, WebDriver driver) {
        try {
            int statuscode = Integer.parseInt(driver.findElement(By.xpath
("/html/body/div/span/span/pre[2]")).getText().substring(9, 12));
            return statuscode;
        } catch (Exception e) {
            e.printStackTrace();
            return 1;
        }
    }
}
  • OPEN DATABASE CODE – Read the columns in the Excel Spreadsheet:
import java.io.File;
import java.io.IOException;
import jxl.Sheet;
import jxl.Workbook;
import jxl.WorkbookSettings;
import jxl.read.biff.BiffException;

public class OpenDataBase{

    Sheet sheet;
    Workbook bdurl;
      
    public Sheet Open(String tablename) throws BiffException, IOException, 
Exception { 

        File file = new File("BD.xls");
            WorkbookSettings ws = new WorkbookSettings();
            ws.setEncoding("Cp1252");
            
            bdurl = Workbook.getWorkbook(new File(file.getCanonicalPath()), 
ws);             
            
            sheet = bdurl.getSheet(tablename);
            return sheet;
        }

    public void Close(Workbook bdurl) throws BiffException, IOException, 
Exception {

        bdurl.close();
    }
}

 

  • MAIN CLASS – statusCode:
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxProfile;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.firefox.internal.ProfilesIni;
import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.remote.DesiredCapabilities;

public class statusCode {

    public static void main(String[] args) throws Exception {
        ProfilesIni profilesIni = new ProfilesIni();
        FirefoxProfile profile = profilesIni.getProfile("default");
        WebDriver driver = new FirefoxDriver(profile);

        DesiredCapabilities capabilities = new DesiredCapabilities();
        capabilities.setCapability(CapabilityType.ACCEPT_SSL_CERTS, true);
        
        profile.setPreference("browser.ssl_override_behavior", 1);
        profile.setPreference("network.proxy.type", 0);
        profile.setAcceptUntrustedCertificates(true);
        profile.setAssumeUntrustedCertificateIssuer(false);

        driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);

        OpenUrl Openurl = new OpenUrl();
        Openurl.OpenURL(driver);
        
        driver.close();
        driver.quit();
    }
}

Thanks guys ! See you next week, if you have any questions, suggestion please feel free to comment below.

Source: https://github.com/rafaelaazevedo/statuscode_nfe

Automated Testing of REST Services

Hello guys, I have been busy these days, for this reason I am a little late.

In my new project I have been working with web-services and I think this could be useful if you want to do automated tests of Rest Services. I summarise the post, because I don’t agree 100 % with this guy. He says that if you are testing the web service you don’t need to test the client, but the behaviours are different, so you still need test the clients, not the data but the behaviour.

Integration tests were more complex task at that time because such tests imply usage of some mock frameworks. Since an API is consumed by different clients (smartphones, desktops…), make sense to gather a group of tests which check a common logic for all types of clients and to highlight the client-specific test scenarios to focus on a client specific logic. The logic works with data which was already tested in API layer. Such approach gives us an amazing testing strategy. We just need to pay all attention to a UI and some specific features instead in different clients: mobile, web, etc. BUT we still need test different clients, the difference is: We don’t need to test the data, just the behaviour of the client 🙂

 

Automated Testing of REST-services

In my experience I have been testing REST-services manual and automated. The framework that we are using here is Abdera to generate the xml and HTTP Client to post the xml on the server. But this guy is using REST-assured library . It is very easy to use and it works well with the most popular java testing frameworks such as TestNG, JUnit and Hamcrest.

@Test
 public void getLandLaordTest() {
 given()
 .contentType(ContentType.JSON)
 .pathParam("id", "DoYlvMXN")
 .when()
 .get("/landlords/{id}")
 .then()
 .statusCode(200)
 .body("firstName", equalTo("Sam"))
 .body("trusted", equalTo(false));
 }

 

Ok, guys it is this, see you next week !

Source: http://java.dzone.com/articles/automated-testing-rest?mz=62823-enterprise-integration

How to read the text from an Alert(ios) in calabash ?

Hi guys,

It’s been a long time that I don’t post anything about calabash and mobile automation, but I found this in my favorites and I realized that could be useful for someone. I am not sure if doesn’t exist another way to read a text inside an Alert, but this is one of the solutions:

Example of step:

Then I see a popup with latitude 10 and longitude 20

 

So,

Then /^I see a popup with latitude (\d+) and longitude (\d+)$/ do |lat, lon| 
  msg = "Latitude:#{lat}\nLongitude:#{lon}" 
  should_see_alert_with_text msg 
end

Finally:

def should_see_alert_with_text (text) 
wait_poll(:until_exists => 'alertView', :timeout => 5) do
  actual = query('alertView child label', :text).first
  unless actual.eql? text 
    screenshot_and_raise "should see alert view with message '#{text}' 
but found '#{actual}'" 
  end 
 end 
end

 

Thank you ! See you 🙂

 

Source: http://stackoverflow.com/questions/16351791/how-do-i-read-text-from-an-alertios-in-calabash

How to update a xml with shell script ?

Hey guys, do you remember the script to create xml with shell script ?

Today I will post an example of shell script to update tags in xml file. I used the script in the bottom link as base.


This is the xml that I used as example:


<?xml version="1.0" encoding="ISO-8859-1"?>
<goodnews>
 
 <to>Rafaela</to>
 <from>Br Bank</from>
 <date>04/01/2007</date>
 <amount>$1000,000.00</amount>
 <account>0024549Y48K3-843</account>
 <message>We are pleased to inform you that the above amount was transferred 
to your bank account</message>
 
</goodnews>  

 

And this is the script:

# !/bin/bash
# Written by rafazzevedo
# http://www.azevedorafaela.wordpress.com

# Uncomment this if you want to use parameters given by #the user and change the variables for the parameters #position like: $1 is the first parameter, $2 is the #second. Instead of tag, xml_file...
# if [ $# -ne 3 ]; then
# echo 1>&2 "Please, use the parameters file, tag and new value."
# exit 127
# fi

xml_file="goodnews.xml"
tag="amount"
new_value="\$1000,000.00"

# We will create a temporary file, just to not modify directly the original one.
temporary="temp_file.temp"

# This space is just to identify the end of the xml.
echo " ">> $xml_file

# Extracting the value from the <$tag> element
tag_value=$(grep "<$tag>.*<.$tag>" $xml_file | sed -e "s/^.*<$tag/<$tag/" | cut -f2 -d">"| cut -f1 -d"<")

echo "Found tag value $tag_value..."

# Replacing element value with $new_value
sed -e "s/<$tag>$tag_value<\/$tag>/<$tag>$new_value<\/$tag>/g" $xml_file > $temporary

echo "Changing $tag to $new_value..."

# Updating the changes to the original file ($xml_file)
chmod 666 $xml_file
mv $temporary $xml_file

Thank you guys ! See you soon 🙂

Source: http://www.dotkam.com/2007/04/04/sed-to-parse-and-modify-xml-element-nodes/

http://stackoverflow.com/questions/19951369/how-to-store-grep-command-result-in-some-variable-in-shell