¿Que se puede hacer para mejorar el rendimiento de las consultas en Oracle 11?

Para mejorar las consultas en oracle nos ayudamos de los indices, los cuales serán mas útil cuanto mas rápido podamos encontrar un registro en él respecto buscarlo en la tabla padre; de aquí se deduce fácilmente que todos los índices que podemos crear no son igual de efectivos, además de que conforme evolucionan los datos dentro de la tabla y por tanto dentro de sus índices, dicha efectividad puede cambiar con el tiempo. Por otro lado, hay ocasiones en los que sale a cuenta mirar directamente la tabla en lugar de pasar antes por el índice.


Tradicionalmente para mejorar el rendimiento de las consultas en SQL contra una BBDD Oracle , una vez optimizada la consulta con técnicas  de Tuneling ( por ejemplo usando Hints si procede) ,   siempre pasamos a dos técnicas clásicas :

  • Reconstruyendo los  índices existente en base de datos ,para lo cual previamente comprobaremos si esos índices deberían de ser reconstruidos  
  • Actualizando estadísticas sobre las tablas que realizamos las consultas.

Veamos mas despacio de que estamos hablando: 

 

Reconstrucción de indices

El índice de una base de datos es una estructura de datos que mejora la velocidad de las operaciones, permitiendo un rápido acceso a los registros de una tabla por lo que se suelen usar sobre aquellos campos sobre los cuales se vayan a realizar búsquedas frecuentes dado que su  funcionamiento es similar al índice de un libro: guardando duplas de elemento que se desea indexar junyo a su posición en la base de datos, de modo que para buscar un elemento que esté indexado, sólo necesitamos que buscar en el índice de dicho elemento para, una vez encontrado, devolver el registro que se encuentre en la posición marcada por el índice.

Los índices pueden ser creados usando una o más columnas, preparando la base de datos tanto para búsquedas rápidas al azar como para ordenaciones eficientes de los registros.

Los índices son construidos sobre árboles B, B+, B* o sobre una mezcla de ellos, funciones de cálculo u otros métodos.

El espacio en disco requerido para almacenar el índice es típicamente menor que el espacio de almacenamiento de la tabla (puesto que los índices generalmente contienen solamente los campos clave de acuerdo con los que la tabla será ordenada, y excluyen el resto de los detalles de la tabla), lo que da la posibilidad de almacenar en memoria los índices de tablas que no cabrían en ella. En una base de datos relacional un índice es una copia de parte de una tabla.

La siguiente consulta SQL mostrará el tamaño en megabytes de un índice determinado, en nuestro caso del índice PK_FACTURACION_CODIGO perteneciente a la tabla FACTURACION y el campo CODIGO del ejemplo. La consulta SQL para mostrar el tamaño ocupado por un índice es la siguiente:

select segment_name, sum(bytes)/1024/1024 MB
from dba_extents
where segment_name = ‘INDICE DE EJEMPLO’
group by segment_name

 

 

Es importante periódicamente examinar y determinar qué índices son susceptibles de ser reconstruidos. Cuando un índice está descompensado puede ser porque algunas partes de éste han sido accedidas con mayor frecuencia que otras dando como resultado problemas de contención de disco o cuellos de botella en el sistema.

Normalmente reconstruimos un índice con el comando ALTER INDEX  (esta sentencia se utiliza para cambiar o reconstruir un índice existente en la base de datos).

Para poder ejecutar este comando el índice debe de estar en el propio esquema donde intentes ejecutarlo o deberías de tener el privilegio alter any index. También tenemos que tener en cuenta que para realizar la reconstrucción de un índice deberíamos de tener cuota suficiente sobre el tablespace que lo lanzamos.

Para reconstruir un índice bastaría con lazar la siguiente sentencia

ALTER INDEX <index_name> REBUILD;

Para reconstruir una partición de un índice podríamos hacer lo siguiente

ALTER INDEX <index_name> REBUILD PARTITION <nb_partition> NOLOGGING;

Nota: En algunos casos cuando alguno de los índices tiene algún tipo de corrupción no es posible reconstruirlo. La solución en este caso es borrar el índice y recrearlo

 

 

 

Actualización de estadísticas

Cuando una base de datos Oracle recibe una sentencia “SQL” para resolver una consulta, se llevan a cabo diversas acciones para lograr la entrega del resultado.

Dentro de los diversos pasos uno de los más importantes es el llevado a cabo por el optimizador basado en costos “Cost Based Optimizer o CBO”. Para que el “CBO” pueda determinar de forma exacta el plan de ejecución de para un “SQL Query” debe disponer de la información de las estadísticas de las tablas e índices que participan en el “SQL Query”, esta información comúnmente es conocida como “Optimizer statistics” “Estadisticas del optimizador”, la misma describe como esta compuesto y distribuido internamente el objeto.

Estas estadísticas son utilizadas por el optimizador para elegir el mejor plan de ejecución para cada instrucción SQL.

El tiempo necesario para colectar las estadísticas en algunos casos puede ser de gran medida. En el manejador se pueden utilizar diversos métodos para tratar de reducir el tiempo de esta tarea en la mayor proporción posible.

 

Es importante tener por tanto actualizadas las estadísticas de la base de datos. Para saber si las estadísticas se están lanzando correctamente podemos hacer una consulta sobre la tabla ALL_INDEXES en oracle 11  (en Oracle 10  es  dba_indexes )y ver el campo last_analyzed para observar cuando se ejecutaron sobre ese índice las estadísticas.

Nota: la columna “LAST_ANALYZED” la cual puede ser encontrada en vistas tales como: “DBA_TABLES”, “DBA_INDEXES”, “DBA_TAB_COL_STATISTICS” indica la fecha en que fue calculada la estadística para dicho objeto por ultima vez.

 

Como ejemplo  , si queremos  saber cuando fue la ultima vez que se ejecutaron estadísticas sobre todas las tablas perteneciente a un determinado esquema de BBDD  lanzaremos la siguiente consulta:

SELECT LAST_ANALYZED,table_name FROM ALL_INDEXES ;

 

 

Como vemos con las fechas podemos  hacernos una idea , de lo actualizado que están las estadisticas   sobre cada tabla   

Para actualizar las estadísticas de forma global para  un  esquema de BBDD,  podemos utilizar  el paquete DBM_STATS  de la la siguiente forma:

Execute DBMS_STATS.gather_schema_stats(‘Esquema’);

 

Una vez actualizadas las estadísticas de los índices de la base de datos lanzamos la siguiente consulta:

SELECT LAST_ANALYZED,table_name FROM ALL_INDEXES ;
SELECT index_name, blevel,
DECODE(blevel,0,'OK BLEVEL',1,'OK BLEVEL',2,
'OK BLEVEL',3,'OK BLEVEL',4,'OK BLEVEL','BLEVEL HIGH') OK
FROM dba_indexes where table_owner='DBPROD08';

 

Con esta sentencia obtendremos el nombre nombre del índice, el blevel y si es correcto este indice.

Los índices que deberíamos de reconstruir son los que en la columna ok aparecen como BLEVEL HIGH.

Blevel (branch level) es parte del formato del B-tree del índice e indica el número de veces que ORACLE ha tenido que reducir la búsqueda en ese índice. Si este valor está por encima de 4 el índice debería de ser reconstruido.

.

Extracción de datos de un pdf desde Java

Veremos como es posible automatizar al extraccion de informacion contenida en un fichero en formato pdf


A veces  necesitamos  extraer información de texto procedente de  ficheros  en formato pdf   por ejemplo  para automatizar la extracción de determinada información  relevante que contengan o simplemente porque deseamos guardar la información editable  en  otro formato mas amigable. En realidad es realmente interesante    intentar   automatizar esta  tarea  pues así nos   evitamos  manipulaciones manuales   y tediosas lo cual seguramente nos hagan perder mucho tiempo  con la gran escasez de este elemento de la vida moderna del que  tampoco disponemos

Vamos  a ver dos métodos  para hacerlo  usando el IDE  de  Eclipse y el lenguaje Java

Método 1; mediante un paso intermedio con conversión previa de los ficheros pdf a ficheros de texto

PDF to TXT Converter es una aplicación de Windows para convertir archivos pdf a archivos de formato de texto sin formato en modo batch. Este programa también admite la conversión de rango de páginas específicas a archivos txt  de modo que después de la conversión, obtendrá el texto editable del documento PDF original

PDF to TXT Converter

Esta  utilidad la podemos descargar   desde  aqui

Hay una pequeña pega con este programa, pues dado que es shareware en la versión gratuita   tiene  bastantes limitaciones ,entre ellas que no se procesaran más de 200 documentos de un sola vez  ( si se intenta con más de esa cantidad   el programa pierde el control)

Un punto a su favor es que permite convertir automáticamente directorios enteros  con contenido de ficheros pdf y de este modo no necesitamos seleccionar  uno a uno cuál de ellos queremos convertir ( pero no olvide que, a no ser que compre la version completa, solo debería contener como máximo 200  ficheros)

Como vemos ,lo interesante de este programa  es que permite convertir los  ficheros pdf a texto  lo cual nos facilitara  procesar estos  muy fácilmente desde   java

Ahora    vamos a   ver un ejemplo    cómo extraer  los metadatos de un documento  de tipo texto obtenido usando el programa anterior

La idea es que a partir de los pdf convertidos previamente a txt , es que iremos  leyendo el contenidos de cada fichero txt e  interpretando las cadenas extraidas de los pdf para buscar  por ejemplo cuatro  meta-datos que necesitamos , haciendo  cuenta del orden en el que aparecen asi como las palabras justo anteriors:
-meta1,
-meta2
-meta3
-meta4:

En esta búsqueda tiene sentido el número de orden en el que aparecen las cadenas anteriores a  la busqueda  pues según el orden en que aparezca corresponderá a un meta-dato u otro.

En este caso meta2 hay que buscarlo dos veces , pero según este orden la cadena siguiente es meta2 o meta4

Veamos el ejemplo;

//*******************************************************
//BLOQUE PRINCIPAL PARA EXTRAER  4 METADATOS de un fichero
//*******************************************************

//abre el fichero
Scanner in =null;
DecimalFormat formateador = new DecimalFormat(“####################”);
Integer contador=0;

//solo procesaremos los ficheros con la extrension txt
if (sub.matches(tipofichero)) try{

in = new Scanner(new FileReader(nombreFichero));
in.useLocale(Locale.ENGLISH);

//lee el fichero palabra a palabra

while (in.hasNext())
{
//lee primera palabra
String palabra = in.next();
caa=palabra;

if (procesa==true)
{
//System.out.println(“Palabra:”+palabra+ “contador=”+contador);

if (contador==1)
{instalacion=palabra;
}
if (contador==5)
{nif=palabra;
}

procesa=false;
contador++;

}

if (caa.matches(“pedido:”))
{
// System.out.println(“<<<Palabra pedido: ENCONTRADA:>>>”);
procesan=true;
contador++;
}

if (caa.matches(“instalación:”))
{
//System.out.println(“<<<<Palabra instalación: ENCONTRADA:>>>>”);//fecha
procesa=true;
contador++;
}

if (caa.matches(“NIF/NIE/Pasaporte:”))
{
// System.out.println(“<<<<Palabra NIF/NIE/Pasaporte: ENCONTRADA>>>>”);
procesa=true;
contador++;
}

//lee números
while (in.hasNextDouble())
{
// lee un double

double d = in.nextDouble();

if (procesan==true)
{
//System.out.println(“Número:”+formateador.format(d)+”contador=”+contador);
if (contador==3)
{pedido=d;
}
if (contador==7)
{linea=d;
}
procesan=false;
contador++;
}

}

} // while (in.hasNext())

//resumen de datos capturados del pdf convertidos en txt y capturada en 4 campos que necesitamos

System.out.println(“Instalacion:”+instalacion); //fecha
System.out.println(“Pedido:”+formateador.format(pedido));
System.out.println(“NIF:”+nif);
System.out.println(“Telefono:”+formateador.format(linea));

….

 

Método 2  directo usando la Liberia ItextPDF

iText es una biblioteca Open Source para crear y manipular archivos PDFRTF, y HTML en Java. Fue escrita por Bruno LowagiePaulo Soares, y otros; está distribuida bajo la Affero General Public License.

El mismo documento puede ser exportado en múltiples formatos, o múltiples instancias del mismo formato. Los datos pueden ser escritos a un fichero o, por ejemplo, desde un servlet a un navegador web.

Más recientemente, ha sido extendida a una biblioteca PDF de propósito general, capaz de rellenar formularios, mover páginas de un PDF a otro, y otras cosas. Estas extensiones son a menudo mutuamente excluyentes. Una clase te permite rellenar en formularios, mientras una clase diferente e incompatible hace posible copiar páginas de un PDF a otro.

El soporte de PDF de iText es, sin embargo, bastante extensivo. Esto soporta firmas basadas en PKI de PDF, cifrado de 40-bit y 128-bit, corrección de colores, PDF/X, gestión de colores por perfiles ICC, y es anfitriona de otras característica

Podemos descargar el fichero itextpdf-5.1.0.jar  desde el respositorio de Maven   o de esta web  http://www.java2s.com/Code/Jar/i/Downloaditextpdf510jar.htm

No debemos olvidar importar el jar desde el Ide  del  Eclipse   en propiedades del proyecto-->Java Build Path–>libraries –>Add External   JARs

Este es un ejemplo  de captura sencilla   de como procesar un pdf directaeente

import java.io.IOException;

import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.parser.PdfTextExtractor;

public class prueba {

/**
* @param args
*/
public static void main(String[] args) {
try {

PdfReader reader = new PdfReader(“d:/ttttt.pdf”);

System.out.println(“This PDF has “+reader.getNumberOfPages()+” pages.”);
String page = PdfTextExtractor.getTextFromPage(reader, 1);
System.out.println(“Page Content:\n\n”+page+”\n\n”);
System.out.println(“Is this document tampered: “+reader.isTampered());
System.out.println(“Is this document encrypted: “+reader.isEncrypted());

reader.close();
} catch (IOException e) {
e.printStackTrace();
}

}

}

Es importante la sentencia String page = PdfTextExtractor.getTextFromPage(reader, 1);  porque si ponemos 0 o un valor superior daría error

Como   gracias  a la librería podemos capturar la informacion del   fichero pdf y aplicar la clase Sacanne para analizar las cadenas     para buscar determinada  informacion  que queremos catalogar vamos a  ver   el ejemplo anterior  usando los mismos campos  y con la misma casuitica:

Este seria el mismo ejemplo de la  primer parte  , pero usando directamente los fichero pdf sin convertir a pdf:

import java.awt.Rectangle;
import java.awt.print.PageFormat;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.text.DecimalFormat;
import java.util.List;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;

import java.io.IOException;

import java.io.FileReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.Scanner;
import java.util.Locale;
import java.io.FileNotFoundException;
import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;

import java.io.StringReader;

import java.util.Locale;
import java.util.Scanner;

import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.parser.PdfTextExtractor;

public class Main {

/**
* @param args
*/

public static void main(final String[] args) throws IOException {

String instalacion=””;
String nif=””;
double pedido=0;
double linea=0;
boolean procesa=false;
boolean procesan=false;
String tipofichero=”pdf”; // solo tomaremos ficheros con esta extension
String caa = substring(20);

String ruta = “D:\temp\ficheros\”;
String ficherox=”28348758018_20181201000002.pdf”;
String fichero=ruta+ ficherox;
System.out.println(“Fichero a analizar =” +ficherox);

int numero=ficherox.length()-3; //calcula donde empieza la extension del fichero
// System.out.println(“numero=”+numero);
String sub =ficherox.substring( numero); //calcula la extension del fichero
System.out.println(“Extension del fichero :”+sub);

//abrimos el PDF
PdfReader reader = new PdfReader(fichero);
int pages = reader.getNumberOfPages();
System.out.println(“Este PDF tiene “+pages+” paginas.”);

//empezamos la coversion a pdf
String page = PdfTextExtractor.getTextFromPage(reader, 1); ////<———————————————–aqui da errror
System.out.println(“Contenido del documento de la pagina “+pages);
System.out.println(“\n\n”+page+”\n\n”);

/////////////////////////////////////////////////////////////////////
//solo procesaremos los ficheros con la extrension pdf
////////////////////////////////////////////////////////////////////

if (sub.matches(tipofichero))

/// try

{

//para procesar el texto lo copiamos a una cadena
String nombreFichero = page ; //Fichero;

System.out.println(“Convertido salida del conversor a un String “);

//borra el scanner
// Scanner in =null;

//formatos de numeros
DecimalFormat formateador = new DecimalFormat(“####################”);

//inicializa conatdor
int contador=0;

//in = new Scanner(new FileReader(nombreFichero));///AQUI ESTA EL PROBLEMA PUES SCANNER NO EST ACEPTADNO EL STRING

Scanner in = new Scanner(nombreFichero);

in.useLocale(Locale.ENGLISH);
System.out.println(“Es correcto el pdfl Convertido salida del conversor a un String “);
//lee el fichero palabra a palabra

while (in.hasNext()) //leemos toda la cadena
{
//lee primera palabra
String palabra = in.next();
caa=palabra;

if (procesa==true)
{
//System.out.println(“Palabra:”+palabra+ “contador=”+contador);

if (contador==1)
{instalacion=palabra;
}
if (contador==5)
{nif=palabra;
}

procesa=false;
contador++;

}

if (caa.matches(“pedido:”))
{
// System.out.println(“<<<Palabra pedido: ENCONTRADA:>>>”);
procesan=true;
contador++;
}

if (caa.matches(“instalación:”))
{
//System.out.println(“<<<<Palabra instalación: ENCONTRADA:>>>>”);//fecha
procesa=true;
contador++;
}

if (caa.matches(“NIF/NIE/Pasaporte:”))
{
// System.out.println(“<<<<Palabra NIF/NIE/Pasaporte: ENCONTRADA>>>>”);
procesa=true;
contador++;
}

//lee números
while (in.hasNextDouble())
{
// lee un double

double d = in.nextDouble();

if (procesan==true)
{
//System.out.println(“Número:”+formateador.format(d)+”contador=”+contador);
if (contador==3)
{pedido=d;
}
if (contador==7)
{linea=d;
}
procesan=false;
contador++;
}

}

} // while (in.hasNext())
in.close();

//resumen de datos capturados del pdf convertido en txt

System.out.println(“Instalacion:”+instalacion); //fecha
System.out.println(“Pedido:”+formateador.format(pedido));
System.out.println(“NIF:”+nif);
System.out.println(“Telefono:”+formateador.format(linea));

//fin de metatados de un documento INDIVIDUAL en el xml

}

}

private static String substring(int i) {
// TODO Auto-generated method stub
return null;
}//CLASS
}

 

 

Con esta librería como vemos  podemos procesar  de un modo relativamente sencilllo   cualquier contenido de un fichero  pdf    de un modo muy potente  como acabamos de ver .

Shell scripting : variables y operaciones

Est post es continuacion de la introduccion a c-shell


Bash , c-shell  o simplemente shell scripting es  un lenguaje de script  creado a fines de la década de 1980 por un programador llamado Brian Fox, que trabajaba para la Free Software Foundation . Fue pensado como una alternativa de software libre para el shell Bourne (de hecho, su nombre es un acrónimo de Bourne Again SHell ), e incorpora todas las características de ese shell, así como nuevas características como la aritmética de enteros y el control de trabajo

Bash es un “shell de Unix”, es decir  una interfaz de línea de comandos para interactuar con el sistema operativo por lo que está ampliamente disponible, siendo el shell predeterminado en muchas distribuciones de GNU / Linux y en Mac OSX, con puertos existentes para muchos otros sistemas.

Además del modo interactivo, donde el usuario escribe un comando a la vez, con ejecución y respuesta inmediatas, Bash (como muchos otros shells) también tiene la capacidad de ejecutar un script completo de comandos, conocido como “Bash shell script” (o “Bash script” o “shell script” o simplemente “script”)  que es justo   lo que vamos a tratar en este post como continuación de un  post anterior introductorio donde exponimos ale tipico Hello world en c-shell

close up code coding computer

Tuberías y sustitución de comandos 

Como hemos visto, el valor de retorno de un comando, tomado estrictamente, es solo un pequeño entero no negativo destinado a indicar el éxito o el fracaso. Su salida real es lo que escribe en el flujo de salida estándar. De forma predeterminada, el texto escrito en el flujo de salida estándar se imprime en el terminal, pero hay algunas formas en que se puede “capturar” y usar como el verdadero valor de retorno del comando.

Tuberías 

Cuando una secuencia de comandos se vincula entre sí en una tubería, la salida de cada comando se pasa como entrada al siguiente. Esta es una técnica muy poderosa, ya que nos permite combinar varios programas de utilidad pequeños para crear algo complejo.

Sustitución de comandos 

La sustitución de comandos es un poco como expansión de variable, pero ejecuta un comando y captura su salida, en lugar de simplemente recuperar el valor de una variable. Por ejemplo, considere nuestro ejemplo de get_password anterior:

#! / bin / bash

función get_password (  ) 
# Uso: get_password VARNAME 
#Le pide al usuario una contraseña; lo guarda como $ VARNAME.
# Devuelve un estado de salida distinto de cero si la entrada estándar no #es un terminal, o si el comando  "leer" devuelve un estado de salida #tinto de cero.
{
  if [[ -t 0 ]] ; then
    read -r -p 'Password:' -s "$1" && echo
  else
    return 1
  fi
}
 
get_password PASSWORD && echo "$PASSWORD"

Realmente no hay razón para que la persona que llama deba guardar la contraseña en una variable.

Si get_password simplemente imprimió la contraseña en su salida estándar, entonces la persona que llama podría usar la sustitución de comandos y usarla directamente:

 #! / bin / bash
 
función get_password (  ) 
# Uso: get_password 
# Pide al usuario una contraseña; Imprime para capturar llamando al código. 
# Devuelve un estado de salida distinto de cero si la entrada estándar no es un terminal, o si 
# salida estándar * es * un terminal, o si el comando "leer" devuelve un 
estado de salida distinto de cero #.
 {
  if [[ -t 0 ]] && ! [[ -t 1 ]] ; then
    local PASSWORD
    read -r -p 'Password:' -s PASSWORD && echo >&2
    echo "$PASSWORD"
  else
    return 1
  fi
}
 
echo "$(get_password)"

Para evaluar "$ (get_password)" , Bash ejecuta el comando get_password en una subshell, capturando su salida estándar y reemplaza $ (get_password) por la salida capturada.

Además de la notación $ (...) , también se admite una notación más antigua  (con comillas inversas), y aún se encuentra con bastante frecuencia. Las dos notaciones tienen el mismo efecto, pero la sintaxis de  es más restrictiva y, en casos complejos, puede ser más complicado acertar.

La sustitución de comandos permite anidar como vemos .  Se permite, expresiones  "$ (b" $ (c) ")" . (es decir ,ejecuta el comando c , usando su salida como un argumento para b , y usando la salida de eso como un argumento para a .)

Una sustitución de comando puede contener una secuencia de comandos, en lugar de un solo comando de modo que se captura la salida de todos estos comandos.

Como hemos visto anteriormente, se pueden usar puntos y coma en lugar de líneas nuevas para separar los comandos, lo cual  es particularmente común en la sustitución de comandos.

Una sustitución de comando puede incluso contener asignaciones de variables y definiciones de funciones (aunque, como los comandos sustituidos se ejecutan dentro de una subshell, las asignaciones de variables y las definiciones de funciones dentro del comando no se verán fuera de ella; solo son útiles si se usan dentro de los comandos sustituidos ).

Aritmética de shell 

Las expresiones aritméticas en Bash se modelan estrechamente en las de C, por lo que son muy similares a las de otros lenguajes derivados de C, como C ++, Java, Perl, JavaScript, C # y PHP.

Una diferencia importante es que Bash solo admite aritmética de enteros (números enteros), no aritmética de punto flotante (decimales y fracciones); Por ejemplo  algo como 3 + 4 significa lo que esperarías (7), pero algo como 3.4 + 4.5 es un error de sintaxis. Algo así como 13/5 está bien, pero realiza una división de enteros, por lo que se evalúa en 2 en lugar de en 2.6.

Expansión aritmética 

Quizás la forma más común de usar expresiones aritméticas es en la expansión aritmética , donde el resultado de una expresión aritmética se usa como un argumento para un comando

. La expansión aritmética se denota $ ((...)) . Por ejemplo, este comando:

 echo  $ ((  3  +  4  *  (  5  -  1  )  ))

impresiones 19 .

expr (en desuso) 

Otra forma de usar expresiones aritméticas es mediante el programa Unix “expr”, que era popular antes de que Bash admitiera las matemáticas.  Similar a la expansión aritmética, este comando:

 echo  ` expr 3 + 4  \ *  \ (  5 - 1  \)  `

impresiones 19 .

Tenga en cuenta que el uso de “expr” requiere un carácter de escape “\” antes del operador de multiplicación “*” y los paréntesis. Tenga en cuenta los espacios entre los símbolos de cada operador, incluidos los paréntesis.

Operadores numéricos

Además de las notaciones familiares + (adición) y - (resta), las expresiones aritméticas también son compatibles con * (multiplicación), / (división entera, descrita anteriormente), % (división de módulo, la operación “resto”; por ejemplo, 11 dividido por 5 es 2 el resto 1, entonces 11% 5 es 1 ), y ** (“exponenciación”, es decir, involución; por ejemplo, 2 4 = 16, entonces 2 ** 4 es 16 ).

Los operadores + y - , además de sus sentidos “binarios” (dos operandos) de “suma” y “resta”, tienen sentidos “unarios” (un operando) de “positivo” y “negativo”. Unary + tiene básicamente ningún efecto; unary - invierte el signo de su operando. Por ejemplo, - (3 * 4) evalúa a -12 , y - (- (3 * 4)) evalúa a 12 .

Referente a variables 

Dentro de una expresión aritmética, se puede hacer referencia a las variables de shell directamente, sin usar expansión variable (es decir, sin el signo de dólar $ ).

Por ejemplo, esto:

 i  =  2 +3
 echo  $ ((  7  * i ))

impresiones 35 . (Tenga en cuenta que primero se evalúa i , que produce 5 y luego se multiplica por 7. Si hubiéramos escrito $ i en lugar de i , se habría realizado una mera sustitución de cadenas; 7 * 2 + 3 es igual a 14 + 3, es decir, 17 – Probablemente no sea lo que queremos.

El ejemplo anterior se muestra usando “expr”:

 i  =  ` expr 2 + 3  ' 
eco  ' expr 7  \ *  $ i  `

impresiones 35 .

Asignación a variables 

Las variables de shell también se pueden asignar dentro de una expresión aritmética. La notación para esto es similar a la asignación de variables regulares, pero es mucho más flexible.

Por ejemplo, el ejemplo anterior podría reescribirse así:

 echo  $ ((  7  *  (  i  =  2  +  3  )  ))

excepto que esto establece $ i a 5 en lugar de a 2 + 3 .

Tenga en cuenta que, aunque la expansión aritmética parece un poco a la sustitución de comandos, se no se ejecuta en un subnivel; este comando realmente establece $ i a 5 , y los comandos posteriores pueden usar el nuevo valor. (Los paréntesis dentro de la expresión aritmética son solo el uso matemático normal de paréntesis para controlar el orden de las operaciones).

Además del operador de asignación simple = , Bash también admite operadores compuestos como + = , - = , * = , / = y % = , que realizan una operación seguida de una asignación. Por ejemplo, ((i * = 2 + 3)) es equivalente a ((i = i * (2 + 3))) . En cada caso, la expresión en su conjunto se evalúa al nuevo valor de la variable; por ejemplo, si $ i es 4 , entonces ((j = i * = 3)) establece tanto $ i como $ j en 12 .

Por último, Bash soporta operadores de incremento y decremento.

El operador incremental ++ incrementa el valor de una variable en 1; si precede al nombre-variable (como el operador “pre-incremento”), entonces la expresión se evalúa al nuevo valor de la variable , y si sigue al nombre-variable (como el operador “post-incremento”), entonces la expresión se evalúa al valor antiguo de la variable .

Por ejemplo, si $ i es 4 , entonces ((j = ++ i)) establece tanto $ i como $ j en 5 , mientras que ((j = i ++)) establece $ i en 5$ j a 4 . El operador de disminución - es exactamente el mismo, excepto que disminuye el valor de la variable en 1. La reducción previa y posterior de la reducción son completamente análogas al incremento previo y al incremento posterior.

Las expresiones aritméticas como sus propios comandos 

Un comando puede consistir completamente en una expresión aritmética, usando cualquiera de las siguientes sintaxis:

 ((  i  =  2 + 3  ))
 let  'i = 2 + 3'

Cualquiera de estos comandos establecerá $ i en 5 . Ambos estilos de comando devuelven un estado de salida de cero (“exitoso” o “verdadero”) si la expresión se evalúa como un valor distinto de cero, y un estado de salida de uno (“falla” o “falso”) si la expresión se evalúa como cero. Por ejemplo, esto:

 ((  0  ))  ||  echo zero
 ((  1  ))  &&  echo non-zero

imprimirá esto:

zero
non-zero

La razón de este comportamiento contraintuitivo es que en C, cero significa “falso” y los valores distintos de cero (especialmente uno) significan “verdadero”. Bash mantiene ese legado dentro de las expresiones aritméticas, luego lo convierte en la convención habitual de Bash al final.

El operador de coma 

Las expresiones aritméticas pueden contener múltiples sub-expresiones separadas por comas , . El resultado de la última sub-expresión se convierte en el valor general de la expresión completa. Por ejemplo, esto:

 echo  $ ((  i  =  2 , j  =  2  + i, i * j ))

establece $ i en 2 , establece $ j en 4 e imprime 8 .

De hecho , la función incorporada admite múltiples expresiones directamente sin necesidad de una coma; por lo tanto, los siguientes tres comandos son equivalentes:

 ((  i  =  2 , j  =  2 + i, i * j ))
 let  'i = 2, j = 2 + i, i * j'
 let  'i = 2'  'j = 2 + i'  'i * j'

Operadores de comparación, booleanos y condicionales 

Las expresiones aritméticas son compatibles con los operadores de comparación de enteros < , > , <= (significado ≤), > = (significado ≥), == (significado =) y ! = (Significado). Cada uno evalúa a 1 para “verdadero” o 0 para “falso”.

También son compatibles con los operadores booleanos:

  • && (“and”), que se evalúan como 0 si cualquiera de sus operandos es cero, y 1 de lo contrario;
  •  ||(“or”), que se evalúa en 1 si alguno de sus operandos es distinto de cero, y en 0 en caso contrario;
  • ! (“not”), que se evalúa en 1 si su operando es cero, y en 0 en caso contrario.

Aparte de que utilizan cero para significar valores “falsos” y valores distintos de cero para significar “verdaderos”, son como los operadores && , || , y ! que hemos visto fuera de expresiones aritméticas. Al igual que esos operadores, estos son operadores “abreviados” que no evalúan su segundo argumento si su primer argumento es suficiente para determinar un resultado. Por ejemplo, (((i = 0) &&(j = 2))) no evaluará el (j = 2)parte, y por lo tanto no establecerá $ j en 2 , porque el operando izquierdo de && es cero (“falso”).

¿Y soportan el operador condicional b ? e1 : e2 . Este operador evalúa e1 y devuelve su resultado, si b es distinto de cero; de lo contrario, evalúa e2 y devuelve su resultado.

Estos operadores se pueden combinar de formas complejas:

 ((  i  =  (  ( a> b && c <d + e ||  f  == g + h ) ? j: k )  ))

Aritmética de bucles 

Arriba, vimos un estilo de for-loop, que se veía así:

# imprimir todos los enteros del 1 al 20: 
for i in {1..20} ; do
  echo $i
done

Bash también admite otro estilo, modelado en los bucles for de C y lenguajes relacionados, usando aritmética de shell:

# imprimir todos los enteros del 1 al 20: 
for (( i = 1 ; i <= 20 ; ++i )) ; do
  echo $i
done

Este bucle for utiliza tres expresiones aritméticas separadas, separadas por punto y coma (y no comas , son expresiones completamente separadas, no solo subexpresiones):

  • La primera es una expresión de inicialización, se ejecuta antes de que comience el bucle.
  • El segundo es una expresión de prueba; se evalúa antes de cada posible iteración del bucle (incluida la primera), y si se evalúa a cero (“falso”), entonces el bucle sale.
  • El tercero es una expresión de conteo; Se evalúa al final de cada iteración de bucle. En otras palabras, este bucle for es exactamente equivalente a este bucle while:
# imprimir todos los enteros del 1 al 20: 
(( i = 1 ))
while (( i <= 20 )) ; do
  echo $i
  (( ++i ))
done

pero, una vez que se acostumbre a la sintaxis, se hace más claro lo que está sucediendo.

Operadores bitwise 

Además de aritméticas y booleanas operadores regulares, Bash también ofrece a los operadores bit a bit “”, significa que los operadores que operan sobre números enteros qua cadenas de bits en lugar de qua enteros.

Si aún no está familiarizado con este concepto, puede ignorarlo de manera segura.

Al igual que en C, los operadores bitwise son :

  • & (bitwise “and”),
  • (bitwise “or”),
  • ^ (bitwise “exclusive or”),
  • ~ (bitwise “not”),
  •  << (desplazamiento a la izquierda en modo bit), y
  • >> (desplazamiento a la derecha en modo bit), así como
  •  & =
  •  | =
  • ^ = (que incluyen la asignación, al igual que + = ).

Literales enteros 

Una constante entera se expresa como un entero literal . Ya hemos visto muchos de estos; 34 , por ejemplo, es un literal entero que denota el número 34.

Todos los ejemplos anteriores  han sido decimales (base diez) literales enteros, que es el valor predeterminado; pero, de hecho, los literales pueden expresarse en cualquier base en el rango 2–64, utilizando el valor de base de notación # (la propia base se expresa en base diez).

Por ejemplo, esto:

 echo $ (( 12 )) # usa el valor predeterminado de base diez (decimal)
 echo $ (( 10 # 12 )) # especifica explícitamente base diez (decimal)
 echo $ (( 2 # 1100 )) # base dos (binario)
 echo $ (( 8 # 14 )) # base ocho (octal)
 echo $ (( 16 # C )) # base dieciseis (hexadecimal)
 eco $ (( 8 + 2 # 100 )) # ocho en base diez (decimal), más cuatro en base dos (binario)

Imprimirá 12 seis veces. (Tenga en cuenta que esta notación solo afecta a cómo se interpreta un literal entero. El resultado de la expansión aritmética todavía se expresa en base diez, independientemente).

Para las bases 11 a 36, ​​las letras inglesas A a Z se usan para los valores de dígitos 10 a 35. Esto no distingue entre mayúsculas y minúsculas. Sin embargo, para las bases 37 a 64, son las letras inglesas en minúsculas las que se usan para los valores de dígitos 10 a 35, mientras que las letras mayúsculas se usan para los valores de dígitos 36 a 61, el signo at @ se usa para las cifras el valor 62, y el subrayado _ se usa para el valor de dígitos 63. Por ejemplo, 64 # @ A3 indica 256259 ( 62 × 64 2 + 36 × 64 + 3 ).

También hay dos notaciones especiales: el prefijo de un literal con 0 indica la base ocho (octal), y el prefijo de 0x o 0X indica la base dieciséis (hexadecimal). Por ejemplo, 030 es equivalente a 8 # 30 , y 0x6F es equivalente a 16 # 6F .

Variables enteras 

Una variable se puede declarar como una variable entera, es decir, su “atributo entero” se puede “establecer”, usando esta sintaxis:

declare -i n
Después de ejecutar el comando anterior, cualquier asignación subsiguiente a n hará que el lado derecho se interprete automáticamente como una expresión aritmética. Por ejemplo, esto:
declare -i n
n='2 + 3 > 4'
 Es más o menos equivalente a esto:
 n  =  $ ((  2  +  3 > 4  ))

excepto que la primera versión declare -incontinuará afectando las tareas posteriores también.

En la primera versión, note el uso de comillas en el lado derecho de la tarea. Si hubiéramos escrito n = 2 + 3> 4 , habría significado “ejecutar el comando + con el argumento 3 , pasando la variable de entorno n configurada a 2 y redirigiendo la salida estándar al archivo 4 “; es decir, establecer el atributo entero de una variable no afecta el análisis global de las declaraciones de asignación, sino que simplemente controla la interpretación del valor que finalmente se asigna a la variable.

Podemos “anular” el atributo entero de una variable, desactivando este comportamiento, usando el comando opuesto:


declare +i n

El comando declare incorporado también tiene otros usos: hay algunos otros atributos que una variable puede tener, y declare tiene algunas otras características además de activar y desactivar los atributos. Además, algunas de sus propiedades destacan:

  • Al igual que con local y export , el argumento puede ser una asignación de variable; por ejemplo, establece el atributo entero de $ n y lo establece en 5 .declare -i n = 2 +3
  • Al igual que con local y export , se pueden especificar múltiples variables (y / o asignaciones) a la vez; por ejemplo, declare -i n=2+3 establece tanto el atributo entero de $ m como el de $ n .
  • Cuando se usa dentro de una función, declare implícitamente localiza la variable (a menos que la variable ya sea local), lo que también tiene el efecto de desarmarla localmente (a menos que se use la sintaxis de asignación).

Aritmética no entera 

Como se mencionó anteriormente, Bash shell arithmetic solo admite aritmética de enteros. Sin embargo, los programas externos a menudo se pueden usar para obtener una funcionalidad similar para valores no enteros.

En particular, la utilidad de Unix común bc se usa a menudo para esto. El siguiente comando:

 echo  "  $ (  echo  '3.4 + 2.2'  | bc )  "

impresiones 5.6 .

No hace falta decir que, dado que bc no está tan estrechamente integrado con Bash como lo es la aritmética de shell, no es tan conveniente; por ejemplo, algo como esto:

# imprimir las potencias de dos, de 1 a 512: 
for (( i = 1 ; i < 1000 ; i *= 2 )) ; do
  echo $i
done

sería, para soportar no enteros, convertirse en algo como esto:

# imprimir los poderes de la mitad, de 1 a 1/512: 
i=1
while [ $( echo "$i > 0.001" | bc ) = 1 ] ; do
  echo $i
  i=$( echo "scale = scale($i) + 1 ; $i / 2" | bc )
done

Parte de esto se debe a que ya no podemos usar un aritmética for-loop; parte de esto se debe a que hacer referencia a las variables y asignarlas a las variables es más complicado ahora (ya que bc no es consciente de las variables de la shell, solo de las suyas, no relacionadas); y parte de ello se debe a que bc se comunica con el shell solo a través de entrada y salida.

!Y  esto  es todo por hoy!Pero no se preocupe en proximos post trataremos otrso temas como entrada/salida, funcionaes complejas y mucho mas

Mas informacion en  https://en.wikibooks.org/wiki/Bash_Shell_Scripting