Tengo que escribir un script bash que cuente símbolos como "<" y ">" de la entrada estándar.
Por ejemplo:
$ ./myscript.sh <example.html
> - 20
< - 21
Found mismatching brackets!
Hice esto:
x=`grep -o '>' example.html | wc -l`
y=`grep -o '<' example.html | wc -l`
if [ "$x" -ne "$y" ]; then
echo "Mismatch!"
fi
echo $x
echo $y
¿Es esta una buena manera? No sé cómo obtener el nombre de archivo "example.html" de la entrada estándar.
Respuesta1
El punto stdin
es que puede ser cualquier cosa, puede ser por ejemplo una tubería, un socket de red, un archivo normal, un dispositivo, puede estar a la mitad de un archivo normal cuando se inicia el script... Si puedes' Si no procesa los datos en una sola pasada, entonces se limita abuscablearchivos, es decir archivos normales y algunos archivos de dispositivo, o tendrá que almacenar la información de alguna manera (en un archivo temporal o en la memoria...). Sin embargo, aquí es posible obtener toda la información a la vez.
Por ejemplo, podrías hacer:
$ grep -o '[<>]' < a.html | sort | uniq -c
82 <
82 >
POSIXly:
fold -w 1 a.html | grep '[<>]' | sort | uniq -c
Para detectar discrepancias:
if fold -w 1 a.html | awk '{n[$0]++}
END{exit(n["<"]!=n[">"])}'
then
echo match
else
echo mismatch
fi
Ahora, para responder la pregunta del tema, en Linux, puedes encontrar un "nombre" para stdin con:
readlink -f /dev/stdin
Ejemplo:
$ readlink -f /dev/stdin < a
/home/chazelas/a
$ : | readlink -f /dev/stdin
/proc/20238/fd/pipe:[758683]
(20238 arriba es el pid de readlink
, por lo que esa ruta no será de mucha utilidad después de readlink
haber salido, y no lo sería de todos modos, eso pipe:[758683]
es solo informativo, no puede serabrió).
Y de manera más general si lsof
está disponible:
lsof -ad0 -p "$$" -Fn 2> /dev/null | sed -n 'n;s/^n//p'
(Sin embargo, $$
al ser el pid del proceso que ejecutó el shell, no funcionaría en subshells a los que se les haya redirigido su stdin)
Ahora, no necesariamente podrá volver a abrir ese archivo para leerlo, e incluso cuando lo haga, es posible que la lectura de ese archivo no le proporcione los mismos datos nuevamente (piense en las tuberías, por ejemplo).
$ seq 3 > a
$ { cat; cat /dev/stdin; } < a
1
2
3
1
2
3
$ cat a | { cat; cat /dev/stdin; }
1
2
3
En Linux, abrir /dev/stdin
si stdin es un archivo normal, leerá el archivo desde el principio nuevamente, mientras que en otros sistemas, abrir /dev/stdin es más como un archivo dup(0)
, es decir, no rebobina el archivo al principio (en el primer ejemplo anterior). , saldría 1\n2\n3\n
una vez en lugar de dos veces).
Respuesta2
Tendrás que almacenar el contenido del archivo de alguna manera. Puedes usar una variable.
content=`cat`
x=`echo "$content" | grep -o '>' | wc -l`
y=`echo "$content" | grep -o '<' | wc -l`
if [ "$x" -ne "$y" ]; then
echo "Mismatch!"
fi
echo $x
echo $y
O un archivo temporal (necesario si example.html
contiene bytes nulos).
tmp=`mktemp`
trap "rm $tmp" EXIT
x=`grep -o '>' "$tmp" | wc -l`
y=`grep -o '<' "$tmp" | wc -l`
if [ "$x" -ne "$y" ]; then
echo "Mismatch!"
fi
echo $x
echo $y
Si leer el contenido del archivo desde la entrada estándar no es un requisito, puede pasar el nombre del archivo como argumento al script.
x=`grep -o '>' "$1" | wc -l`
y=`grep -o '<' "$1" | wc -l`
if [ "$x" -ne "$y" ]; then
echo "Mismatch!"
fi
echo $x
echo $y
Llame al script así:
$ ./myscript.sh example.html
Respuesta3
Una posibilidad para su tarea es:
#!/bin/bash
if [[ -n $1 ]]; then
if [[ ! -f $1 ]] || [[ ! -r $1 ]]; then
echo >&2 "File \`$1' not found or not readable."
exit 1
fi
exec "$0" < "$1"
fi
declare -A hary
while read c; do
(( ++hary[$c] ))
done < <(grep -o '[<>]')
echo "> ${hary[>]}"
echo "< ${hary[<]}"
Si llamas a este scriptrecuento no coincidente(puedes elegir un nombre más corto), podrás usarlo con o sin nombre de archivo. Algunas posibilidades:
$ countmismatched example.html
$ countmismatched < example.html
$ cat example.html | countmismatched
La salida será algo como:
> 41
< 42
Si necesita detectar discrepancias, agregue al final del script:
if (( hary[<]} != hary[>] )); then
echo "Mismatched brackets"
else
echo "It's all good"
fi
o algo más explícito:
((difference=hary[<]-hary[>]))
if (( difference>0 )); then
echo "Mismatched brackets: you have $difference more <'s than >'s"
elif (( difference<0 )); then
echo "Mismatched brackets: you have $((-difference)) more >'s than <'s"
else
echo "It's all good"
fi