User Tools

Site Tools


informatica:linux:script

This is an old revision of the document!


Table of Contents

Parámetros pasados a un script

$0	Contiene el nombre del script tal como es invocado
$*	El conjunto de todos los parámetros en un solo argumento
$@	El conjunto de argumentos, un argumento por parámetro
$#	El número de parámetros pasados al script 
$?	El código de retorno del último comando
$$	El PID del shell que ejecuta el script
$!	El PID del último proceso ejecutado en segundo plano
basename $0 devuelve el nombre del script
dirname $0  devuelve el nombre del directorio

Mostrar todos los parámetros pasados a un script:

#!/bin/bash
for i in $@
do
  echo $i
done

O metiéndolos en un vector:

#!/bin/bash
argv=("$@")
let narg=("$#")-1
for i in `seq 0 $narg`
do
  echo ${argv[$i]}
done

Coger opciones de parámetros pasados a un script:

http://stackoverflow.com/questions/192249/how-do-i-parse-command-line-arguments-in-bash

https://github.com/oracle/docker-images/blob/master/OracleWebLogic/dockerfiles/buildDockerImage.sh

Leer variables

Vector

Se define un vector

array=( hola adios )

Tambien se puede definir:

variable=$(echo hola adios)
array=( $variable )

Se muestra un número de elemento en concreto:

# echo ${array[0]}
hola

# echo ${array[1]}

adios

Número de elementos:

# echo ${#array[@]}
2

Todos los elementos:

# echo ${array[@]}
hola adios

Método dos

array[0]='cero'

echo ${array[0]}

Variable en el nombre de un vector

Funciona incluso con elementos con espacios

#!/bin/bash
var=Error
vector_Error=( "hola que tal" "como estamos" "bastante bien" )

eval name=(\"\${vector_$var[@]}\")

echo ${name[0]}
hola que tal

echo ${name[1]}
como estamos

echo ${name[2]}
bastante bien

Duplicar vector

prueba=(
'hola que tal como estamos'
'pues bastante bien'
'¿para la edad que tienes?'
)

Funciona:
nuevo=("${prueba[@]}")

echo ${prueba[1]}
pues bastante bien

echo ${nuevo[1]}
pues bastante bien

Convertir linea en vector con un separador

Si tenemos la linea:

linea="hola;que tal;como estamos;bien"

Y queremos pasar a un vector cortando por el caracter “;” y meterlo en el vector “array”

IFS=';' read -a array <<< "$linea"

Lo comprobamos recorriendo el vector:

let n=${#array[@]}-1
for i in `seq 0 $n`
do 
  echo ${array[$i]}
done

Otra forma de recorrer el vector es esta. Muy importante las por que si no coge mal los valores con espacios:

for i in "${array[@]}"; do echo $i; done

El resultado es:

hola
que tal
como estamos
bien

while read linea

while read linea
do
 texto=$texto" "$linea
done < <(cat fichero)
echo $texto

read dentro de bucle

while read linea
do
  read input </dev/tty
done < <(cat fichero)

Asignar Variable

Asignar variable a un nombre de variable:

#!/bin/bash
var=variable2
variable2="hola"
eval echo \$${var}
#o lo que es lo mismo
echo $variable2

Asignar variable a la misma variable con nombre variable :P

#!/bin/bash
var=variable2
for i in 3 2 1
do
  eval $var='`echo ${!var}`" "$i'
done
eval echo \$${var}
echo ${!var}
echo $variable2

Bucle

if

if [ $var -eq 1 ]
then
  echo "var=1"
elif [ $var -eq 2 ]
then
  echo "var=2"
else
  echo "var no vale 1 ni 2"
fi

for

for ((i=1;i<=9;i+=1)); do echo $i; done
for i in {1..9}; do echo $i; done
for i in `seq 1 9`; do echo $i; done

case

case $var in
  0 ) echo "Salir del menu"; exit;;
  1 ) echo "Opcion 1";;
  2 ) echo "Opcion 2" ;;
  * ) echo "Opción incorrecta" ;;
esac

Comprobar si un puerto está abierto o levantado:

En local:

#!/bin/sh
while true 
  do
  if ! netstat -an | grep 78889 > /dev/null
  then
    echo `date` >> /var/log/port8889.log
  fi
done

  lsof -Pni:[puerto]

  netstat -putan | grep [puerto]

Servidor externo

Con estos comandos a veces hay que poner el nombre del servicio, por ejemplo una VIP de oracle

cat < /dev/null > /dev/tcp/<ip>/<puerto>

Conexión correcta, no da error:

# cat < /dev/null > /dev/tcp/oracle-scan/1521
# echo $?
0

Conexión fallida, devuelve error:

# cat < /dev/null > /dev/tcp/oracle-scan/1521
-bash: connect: Conexión rehusada
-bash: /dev/tcp/oracle-scan/1521: Conexión rehusada

Lo mismo para

echo > /dev/tcp/<ip>/<puerto>

Conexión correcta, no da error:

# echo > /dev/tcp/oracle-scan/1521
# echo $?
0

Conexión fallida, devuelve error:

# echo > /dev/tcp/oracle-scan/15212
-bash: connect: Conexión rehusada
-bash: /dev/tcp/oracle-scan/15212: Conexión rehusada

Con netcat, la respuesta es parecida al telnet:

nc oracle-scan 1521

Demonio

Se crea un fichero kkfichero que mientras este creado se ejecuta el script.
Se ponen las dos condiciones, la que queremos i el fichero para poder parar el script (en este ejemplo contamos hasta 10)

#!/bin/sh
touch kkfichero
i=0

while [ $i -le 10 ] && [ -f kkfichero ]
do
  echo $i
    i=$(($i + 1))
  sleep 3
done

if [ -f kkvolei ]
then
  rm kkvolei
fi

Para que el fichero sea único se puede crear con el PID:

#!/bin/sh
PID=`echo $$`

Realiza comprobación

test `date +\%w` = 3 && echo "Hoy es miércoles"
`comando`; [ $? -eq 0 ] && echo "Comando correcto"

Leer fichero:

cat fichero.txt | while read linea
do
  echo "LEIDO ($linea)"
done

Ver si URL existe

Opción 1

#!/usr/bin/bash.exe

listaUrls=`cat<<EOF
http://www.google.com/index.html

http://www.voleicat.net/generic/documentsweb0607/resultats/campionats%20catalunya/102/cl_102_16.htm

EOF`
for url in $listaUrls
do
  tot=`wget $url 2>&1| grep -ic 'Not Found'`

  if [ $tot -ne 0 ]
  then
    echo "$url NO EXISTE"
  else
    echo "$url SI EXISTE"
  fi
done

Opción 2

curl -w %{http_code}   -s  -o fichero http://www.google.com

Devuelve el código 404,300, 200, etc….
Parámetros:
-s: silent Para no mostrar el progreso de descarga
-o fichero: Para descargar la salida en un fichero. El fichero puede ser /dev/null

Conocer mi ip

miip=`curl -s http://checkip.dyndns.org | awk '{print $6}'  | cut -d "<" -f1`

ls i espacio en disco

Muestra lo que ocupan unos ficheros por fecha

#!/bin/sh
ultima=`du -a --time | sort -k 2 | head -1 | awk '{print $2}' | cut -b -7`
let total=0
let sum=0
#du -a --time | sort -k 2 | while read linea
IFS=$'\n'
for linea in `du -a --time | sort -k 2`
do
  fecha=`echo $linea | awk '{print $2}' | cut -b -7`
  let sum=`echo $linea | awk  '{print $1}'`
  if [ $ultima == $fecha ]
  then
    let total=total+sum
  else
    echo $ultima $total
    let total=$sum
  fi
  ultima=$fecha
done
echo $ultima $total

Muestra lo que ocupan los directorios

# du --max-depth=1

Búsqueda de ficheros

Ficheros modificados hace mas de 7 días

find * -mtime +7 -exec ls -la {} \;

Ficheros modificados en los últimos 7 dias

find * -mtime -7 -exec ls -la {} \;

Mover los ficheros

find * -mtime +7 -maxdepth 0 -type f -exec mv '{}' old/ ';'

Operaciones

Suma

En bash:

let i=$i+1
expr 1 + 1

En sh:

i=$(($i + 1))

Búcles

comparaciones

  -eq is equal to 5 == 6
  -ne is not equal to 5 != 6
  -lt is less than 5 < 6
  -le is less than or equal to 5 <= 6
  -gt is greater than 5 > 6
  -ge is greater than or equal to 5 >= 6
  
  -f es un archivo (existe el fichero)
  -d es un directorio

Para la comparación entre cadenas se usara los siguientes simbolos:

  == Realiza la comparación entre cadenas si son iguales
  != Decide si son distintas
  -n Informa si la cadena tiene longitud mayor a cero
  -z Informa si la cadena tiene longitud igual a cero

SED

Mostrar un parámetro

echo "param1=hola param2=que param3=tal" | sed -e 's/^.*param1=\([^ ]*\).*$/\1/'

Mostrar una parte de un fichero

sed -n -e '/<pattern start>/,/<pattern end>/p' <file>

Si no ponemos <pattern end> nos muestra hasta el final.
Para mostrar hasta el final:

tail -n +`grep  "<patern>" messages -n | head -1 | awk -F: {'print $1'}` <file>

Para mostrar determinadas líneas

Mostrar la segunda línea

sed -n '2p' file.txt

Mostrar hasta la línea 20:

 sed 20q file.txt
 

Mostrar de la 10 a la 33:

sed -n '10,33p' file.txt

Mostrar de la 10 a la 33, pero mas rápido por si el fichero es muy grande. En la q le dices cuantas líneas quieres que te muestre y luego sobre esas 34 cogemos de a 10 a la 33:

sed -n '34q;10,33p' file.txt

La linea 15 y la 20

sed -n '15p;20p' file.txt

Referencias:
http://stackoverflow.com/questions/6022384/bash-tool-to-get-nth-line-from-a-file
http://sed.sourceforge.net/sed1line.txt

Remplazar cadena en un fichero

Fichero base:

<input-fields>
   <data-value name="BEAHOME" value="/u01/weblogic/mid1036" />
   <data-value name="USER_INSTALL_DIR" value="/u01/domains/wls" />
   <data-value name="INSTALL_NODE_MANAGER_SERVICE" value="no" />
   <data-value name="COMPONENT_PATHS" value="WebLogic Server" />
</input-fields>

Substituir el campo value para cada name de lo que hay entre comillas.

sed -i '/BEAHOME/s/value="[^"]*"/value="{{ bea.home }}"/' silent.xml

Resultado:

 <data-value name="BEAHOME" value="{{ bea.home }}" />

Cambiar por el mismo campo que pone en name En \1 guarda la variable y substituye el campo value por ella:

sed -e '/<data-value/s/name="\(.*\)" *value="[^"]*"/name="\1" value="{{ \1 }}"/' file.xml

Substituir después del = todo lo que haya.

[ENGINE]
Response File Version=1.0.0.0.0
[GENERIC]
ORACLE_HOME=/opt/middleware
INSTALL_TYPE=Weblogic Server
MYORACLESUPPORT_USERNAME=
MYORACLESUPPORT_PASSWORD=
DECLINE_SECURITY_UPDATES=true
SECURITY_UPDATES_VIA_MYORACLESUPPORT=false
PROXY_HOST=
PROXY_PORT=
PROXY_USER=
PROXY_PWD=
COLLECTOR_SUPPORTHUB_URL=
sed -e '/ORACLE_HOME=/s/ORACLE_HOME=\(.*\)/ORACLE_HOME=\/u01\/mid12212/' response.rsp

Resultado:

ORACLE_HOME=/u01/mid12212

Mostrar un trozo de cadena limitado por dos cadenas

echo "hola como estamos todos hoy" | sed -e 's/^.*como\([^*]*todos\).*$/\1/'

devuelve (con un espacio al principio)

 estamos todos

Substituir cadena por variable

sed -i "s|cadena|$variable|" fichero

Contar veces que aparece una ip en un fichero de acceso

cat jur | sort | uniq | while read linea; do echo `cat jur | grep $linea | wc -l` " " $linea >> jur2;done; cat jur2 | sort -n

Funcion

Simple

#!/bin/bash 
function quit {
exit
}  
function e {
echo $1 
}  
e Hello
e World
quit
echo foo 

La Salida es “Hello World” y sale sin hacer el foo

Devuelve un vector

#!/bin/bash
funcion()
{
  salida[1]=hola
  salida[2]=adios
  echo "${salida[@]}"
}

devuelto=( `funcion` )
echo ${devuelto[0]}
echo ${devuelto[1]}

AWK

-Muestra la última columna

cat fichero.txt | awk {'print $NF'}

-Muestra toda la cadena

cat fichero.txt | awk {'print $0'}

-Muestra des de la columna n hasta la última (en la d ponemos el delimitador):

#echo "hola que tal como estamos" | cut -d" "  -f2-
que tal como estamos
# echo "hola-que-tal-como-estamos" | awk -F\- '{for(i=1;i<=NF-2;i++){printf "%s-", $i}; printf $(NF-1)"\n"}'
hola-que-tal-como

-Usa delimitador <>

# echo "Hola que tal <como estamos> mas texto <dentro etiqueta>" | awk -F "[<>]" '{print $3}'
mas texto 

-Condiciones
Busca la palabra cadena en la columna dos y muestra toda la linea

awk '{if ( $2 == "cadena" ) printf $0"\n"}' fichero

Para buscar un texto en una columna y evitar el cutre grep | grep -v grep. Busca que en la columna del proceso este la palabra gnome y muestra todo el proceso:

ps -ef | awk '{if (index($8,"gnome") >0 ) printf $0"\n"}'

-Variables dentro de awk:

root="/webroot"
echo | awk -v r=$root '{ print "shell root value - " r}'

Recorre todos los valores uno a uno

awk '{ for(i = 1; i <= NF; i++) { print $i; } }' fichero.txt

CUT

De la segunda hasta el fin

echo "hola que tal como estamos" | cut -d" "  -f2-
que tal como estamos

De la cuarta hasta el inicio

echo "hola que tal como estamos" | cut -d" "  -f-4
hola que tal como

Muestra número de carácteres después de concurrencia

awk 'c-->0;$0~s{if(b)for(c=b+1;c>1;c--)print r[(NR-c+1)%b];print"";print"***************";print;c=a}b{r[NR%b]=$0}' b=lineas_antes a=lineas_despues s="concurrencia" fichero.txt

Muestra un número de columna guardado en una variable

# t=2
# echo "hola que tal estamos" | awk -v i=$t '{print $i}'

que

GREP

Varias coincidencias

# grep -E 'hola|adios' fichero.txt

Buscar por nombre de proceso, para evitar grep -v grep

# ps -fc java

Sacar excepciones en SystemOut.log

Saca toda la excepción hasta que la siguiente linea empiece con el formato corchete fecha:

[12/9/11 6:34:05:553 CET]

Uso:

./script.sh " E "
#!/bin/bash
while read linea
do
  echo $linea
  let numero=`echo $linea | awk -F\: {'print $1'}`+1
  primer=`sed -n "$numero p" SystemOut.log | grep -v "^\["`
  while [ "$primer" != "" ]
  do
    echo $primer
    let numero=$numero+1
    primer=`sed -n "$numero p" SystemOut.log | grep -v "^\["`
    let numero=$numero+1
  done
echo
echo
done < <(grep -n "$1" SystemOut.log)

Eliminar carácteres de un fichero binario

tr -cd '\11\12\15\40-\176' < fichero_binario > limpio.txt

contraseñas aleatorias

tr -dc A-Za-z0-9+-_ < /dev/urandom | head -c 8;echo
strings /dev/urandom | grep -o '[[:alnum:]]' | head -n 8 | tr -d '\n'

SSH automático con expect

Los comandos principales son:

expect: continua cuando recibe esa cadena
send: envía una cadena
interact: salta al prompt
export: asigna variable

Si no responde a un expect, salta a la siguiente linea por el timeout que tenga puesto, por defecto 8-10 segundos.

#!/usr/bin/expect -f
spawn ssh usuario@maquina
expect "*?assword:*"
send "password_usuario\r"
expect " > "
interact

Con variable:

#!/usr/bin/expect -f
export contrasenya password_usuario 
spawn ssh usuario@maquina
expect "*?assword:*"
send "$contrasenya\r"
expect " > "
interact

Asignar parámetros: set param1 [lindex $argv 0] set param2 [lindex $argv 1]

En los send es recomendable poner - - por si alguna contraseña viene en variables con un guión al principio:

send -- "$password\r"

Para evitar la validación del fingerprint realizar el ssh con el parámetro:

  1. o StrictHostKeyChecking=no

Condiciones en Expect

Por ejemplo, si la máquina no la tenemos en known_hosts nos dirá antes la pregunta:

Are you sure you want to continue connecting (yes/no)?

Para poner una condición en expect:

expect {
   "Are you sure you want to continue connecting (yes/no)?" { send "yes\r"; exp_continue }
   "*?assword:*" {send "password\r"}
}

Traza en Expect

exp_internal 1

Bucles con Expect

#!/bin/bash
lista=`cat <<EOF
fichero1
fichero2
EOF`
for fichero in $lista; do
/usr/bin/expect <<EOF
spawn scp $fichero usuario@servidor:
expect *assword*
send contrasenya\r
expect *#
EOF
done

Login con contraseña encriptada

Mirar http://wiki.legido.com/doku.php?id=informatica:linux:script&#encriptar_contrasenas para ver como se encripta contraseña

#!/bin/bash
password=`echo U2FsdGVkX19VVheWdlKL5do97riAmAUq | openssl enc -base64 -d | openssl enc -des3 -k 1469 -d`
/usr/bin/expect <<EOF
spawn ssh usaurio@maquina
expect "*?assword:*"
send "$password\r"
send "\r"
expect "*:~$*"
send "touch jurjur\r"
expect "*:~$*"
send "exit\r"
EOF

Insertar texto en un fichero después de una linea buscada

#!/bin/bash
texto_busqueda="http://guifi.net"
texto_insertar="*********"
inicio=0

grep -n "$texto" $1 | while read linea
do
    num=`echo $linea | awk -F\: {'print $1'}`
    let diff=$num-$inicio
    head -$num $1 | tail -$diff
    echo $texto_insertar
    let inicio=$num
done

Tabla de instrucciones dependiendo de la shell ejecutada

Trucos

Como curiosidad nunca uses:

$(cat algo)

Utiliza:

$(< algo)

Es infinitamente más rápido. De hecho es RARA la vez que hay que usar cat en un shell script. Siempre hay mejores alternativas.

Y tampoco uses 'let', es feo. Usa:

((CONTADOR++))

Escribe la salida del último comando # echo $(!!)

Liberar espacio fichero borrado pillado por un proceso

A veces borramos sin querer un fichero pillado por un proceso y vemos que no libera el espacio:

# df -h /tmp
/tmp 4.9G  4.6G     0 100% /tmp
# ls -la /tmp

-rw-r----- 1 ruth ruth 1.2G Feb 27 10:36 005c81d2-1a5b-449a-aa0b-13a36509bd6a.zip

Borramos el fichero pero no se libera espacio:

# df -h /tmp
/tmp 4.9G  4.6G     0 100% /tmp

Vemos que proceso tiene pillado el fichero

# /usr/sbin/lsof  | grep 005c81d2-1a5b-449a-aa0b-13a36509bd6a.zip
COMMAND     PID     USER   FD      TYPE             DEVICE   SIZE/OFF       NODE NAME
java      17923 ruth  777w      REG              253,2          0      98310 /tmp/005c81d2-1a5b-449a-aa0b-13a36509bd6a.zip (deleted)

Si no podemos parar el proceso por ser crítico, truncamos el fichero. El primer número es el PID del proceso el segundo el FD:

# > /proc/17923/fd/777

Ahora ya está libre el espacio

Fuente: http://www.unixwerk.eu/linux/deleted_files.html

XARGS

ls | xargs -i mv {} {}_old

Date

http://www.cyberciti.biz/tips/linux-unix-get-yesterdays-tomorrows-date.html

date --date='tomorrow'
date --date='1 day'
date --date='10 day'
date --date='10 week'
date --date='10 month'
date --date='10 year'
date --date='yesterday'
date --date='1 day ago'
date --date='10 day ago'
date --date='10 week ago'
date --date='10 month ago'
date --date='10 year ago'

Formato fecha custom:

date +%Y%m%d_%H%M%S
20190420_225307

Poner fecha en historial history

export HISTTIMEFORMAT='%d/%m/%Y %H:%M:%S: '

Salir terminal sin grabar history

kill -9 $$

Unixtime

Escribir unixtime:

# date +%s

1384245790

Pasar unixtime a fecha texto

# date -d '@1384245790' +%d

date -d '@1384245790' +%d/%m/%Y

Pasar de formato texto a unixtime:

# date +%s -d"Jan 1, 1980 00:00:01"
315529201
# date +%s -d"Jan  3 08:22:12 2015 GMT"
1420273332

Para usar strftime hay que tener instalado el paquete gawk

convertir fecha:

sed -r 's/(\[|])//g' | awk ' { $1=strftime("%D %T",$1); print }'

Ejemplo:

# echo 1339506985|awk '{print strftime("%D %T",$1)}'

06/12/12 15:16:25

http://influencd.co.uk/blog/2011/03/converting-unix-epoch-time-in-bash-history/

Encriptar contraseñas

Para encriptar una contraseña. mi_salt puede ser numero o palabra. Sin ese valor no se puede desencriptar:

# echo -n "mi_contraseña" | openssl enc -des3 -k <mi_salt> | openssl enc -base64

U2FsdGVkX1/js2CVDYiDj4H4MAt4TETG

Para desencriptar:

# echo "U2FsdGVkX19bIX1uNHDlZniiqz51ggEW" | openssl enc -base64 -d | openssl enc -des3 -k <mysalt> -d

Por ejemplo:

# echo -n "jurjur" | openssl enc -des3 -k 14 | openssl enc -base64
U2FsdGVkX1+kzYuThdOeyeUnyXWpj9o0
# echo "U2FsdGVkX19bIX1uNHDlZniiqz51ggEW" | openssl enc -base64 -d | openssl enc -des3 -k 14 -d
jurjur

Cadenas de texto

Sacar la posición de una subcadena:

Con variable:

string="hola que tal como estamos"
# echo | awk '{ print index("'"${string}"'", "tal")}' 
10
# echo | awk '{ print index("'"hola que tal como estamos"'", "como")}'
14

Pedir variable que no se ve en el prompt

Con esta opción no escribe nada:

read -s variable

Script que escribe asteriscos:

#!/bin/bash

unset PASSWORD
unset CHARCOUNT

echo -n "Enter password: "

stty -echo

CHARCOUNT=0
while IFS= read -p "$PROMPT" -r -s -n 1 CHAR
do
    # Enter - accept password
    if [[ $CHAR == $'\0' ]] ; then
        break
    fi
    # Backspace
    if [[ $CHAR == $'\177' ]] ; then
        if [ $CHARCOUNT -gt 0 ] ; then
            CHARCOUNT=$((CHARCOUNT-1))
            PROMPT=$'\b \b'
            PASSWORD="${PASSWORD%?}"
        else
            PROMPT=''
        fi
    else
        CHARCOUNT=$((CHARCOUNT+1))
        PROMPT='*'
        PASSWORD+="$CHAR"
    fi
done

stty echo

echo
echo $PASSWORD

Repositorio

Para evitar que valide el certificado de un repositorio, por ejemplo si estamos detrás de un proxy:

echo "Acquire::https::pkg.jenkins.io::Verify-Peer "false";" > /etc/apt/apt.conf.d/jenkins

ip a s

ip addr add 192.168.56.101/24 dev eth0

ip link set eth1 up

Generar un número aleatorio random en un rango

awk -v min=0 -v max=100000 'BEGIN{srand(); print int(min+rand()*(max-min+1))}'

Entre 4 y 10

shuf -i4-10 -n1

vim vi

yy copia línea
p pega línea
v selecciona
y copia
p pega

Formatear yaml json

:%!python -m json.tool

Formatear xml

:%!xmllint --encode UTF-8 --format -

Carácteres especiales

Para ver los carácteres especiales:

:set list

Vi añade newline al final de fichero. Si obtenemos el error No newline at end of file al hacer un diff por ejemplo, podemos hacer esto para verlo, aparecerà un \n en uno de los ficheros al final:

od -c fichero

Buscar paquete debian

# apt-file search /usr/bin/identify
graphicsmagick-imagemagick-compat: /usr/bin/identify

O desde la web:
https://www.debian.org/distrib/packages#search_contents

informatica/linux/script.1569321511.txt.gz · Last modified: 2019/09/24 10:38 by jose