Bash script para imprimir archivos que tienen una cierta longitud de línea

3

Soy nuevo en esto ... Necesito un script que reciba un directorio como parámetro e imprima los nombres de los archivos que tienen entre 15 y 50 líneas, pero no sé cómo usar la wcsalida en una ifsecuencia:

#!/bin/bash
for f in `ls`; do
  echo "File -> $f"
  cat $f | wc -l
done

¿Cómo recibo un parámetro? ¿Y cómo asigno una wcsalida a una variable?

jaundavid
fuente
2
En primer lugar, no lo hagas for f in `ls`, por favor. Simplemente use for f in *. Ver: mywiki.wooledge.org/ParsingLs
slhck

Respuestas:

7

Los parámetros pueden ser recibidos como $1, $2, etc., o $*para todos ellos.

También está la matriz $@, que generalmente se usa "$@"como una mejor versión de $*. Otro uso posible es el ${@:3}que significa "todos los argumentos que comienzan con 3rd".

Para obtener la salida de un comando, use $( ... )o su forma anterior ` ... `. En general, se recomienda usar siempre,$() ya que puede estar anidado, por ejemplo blah=$(cat /blah/$(blah)/blah).

#!/usr/bin/env bash

for dir in "$@"; do
    for file in "$dir"/*; do
        lines=$(wc -l < "$file")
        if (( lines >= 15 && lines <= 50 )); then
            echo "File -> $file ($lines lines)"
        fi
        # another possible syntax:
        # if [ "$lines" -ge 15 ] && [ "$lines" -le 50 ]
        # or:
        # if [[ "$lines" -ge 15 && "$lines" -le 50 ]]
    done
done

Recursos utiles:

Gravedad
fuente
Para el registro, BSD wc(al menos el que tengo en OS X) rellena el recuento de líneas con espacios iniciales incluso cuando lo hace wc -l < "$file", lo que necesita un trabajo de análisis adicional.
slhck
1
También puede usar el formulario en for dir dolugar defor dir in "$@"; do
evilsoup
@slhck: bash ignora automáticamente los espacios en blanco iniciales y finales si le pide que use la variable como un entero (por ejemplo, comparaciones) o si lo hace explícitamente declare -i.
Grawity
Correcto, excepto que imprime File -> ./foo ( 47 lines)o similar, pero eso es un problema menor.
slhck
@slhck: Eso se puede evitar usando declare -io let.
Grawity
3

Aquí hay otra forma de hacerlo usando awk:

#!/usr/bin/env bash
for dir in "$@"; do
  for file in "$dir"/*; do 
    awk 'END{if(NR>=15 && NR<=50){print FILENAME}}' "$file"; 
  done
done

EXPLICACIÓN: awk(y sus muchas variantes como gawko mawketc.) leen un archivo línea por línea. La variable NRes el número de línea actual. Se END{}ejecutará un bloque cuando se llegue a la última línea del archivo de entrada, en cuyo punto NRserá el número de líneas en el archivo. Finalmente, FILENAMEes el nombre del archivo que se está procesando actualmente. Entonces, el script imprime el nombre del archivo si el número de líneas que ha visto está entre 15 y 50.

Del mismo modo, puedes hacer esto con Perl:

#!/usr/bin/env bash
for dir in "$@"; do
  for file in "$dir"/*; do 
      perl -ne 'END{if($. >=15 && $. <=50){print "$ARGV\n"}}' "$file"
  done
done

EXPLICACIÓN: perl -netambién leerá un archivo línea por línea. En Perl, el número de línea actual se almacena $.y el ENDbloque funciona de la misma manera que en awk. $ARGVes el argumento pasado en la línea de comando, el nombre del archivo que se imprimirá solo si tiene el número correcto de líneas.

Probablemente, su mejor opción sea bash y wc respuesta de @grawity, pero la wcimplementación no afectará a estos y debería funcionar bien en cualquier * nix.

terdon
fuente
Si $.es el número de línea, ¿cuál es $,? (El nuevo operador "smartmatch" debería funcionar como $. ~~ 15..50, aunque necesito probarlo)
Grawity
@grawity, sí, ~~puede funcionar, pero no estoy muy familiarizado con él y es mucho más esotérico. Pensé que daría una respuesta más simple.
terdon
2

Generar algunos archivos:

$ for i in `seq -w 1 60`; do seq -w 1 $i > $i.lines; done

Imprima solo aquellos que cumplan los criterios:

$ wc -l * | awk '$1 >= 15 && $1 <= 50 { print $2 }' | column
15.lines        23.lines        31.lines        39.lines        47.lines
16.lines        24.lines        32.lines        40.lines        48.lines
17.lines        25.lines        33.lines        41.lines        49.lines
18.lines        26.lines        34.lines        42.lines        50.lines
19.lines        27.lines        35.lines        43.lines
20.lines        28.lines        36.lines        44.lines
21.lines        29.lines        37.lines        45.lines
22.lines        30.lines        38.lines        46.lines
Ярослав Рахматуллин
fuente