Bash, nome do arquivo stdin?

Bash, nome do arquivo stdin?

Eu tenho que escrever um script bash que contará símbolos como "<" e ">" do stdin.

Por exemplo:

$ ./myscript.sh <example.html
> - 20
< - 21
Found mismatching brackets!

Eu fiz isso:

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

Este é um bom caminho? Não sei como obter o nome do arquivo “example.html” do stdin.

Responder1

O ponto principal stdiné que pode ser qualquer coisa, pode ser, por exemplo, um cano, um soquete de rede, um arquivo normal, um dispositivo, pode estar no meio de um arquivo normal quando seu script é iniciado ... Se você puder ' Se você não processar os dados de uma só vez, então você se limita apesquisávelarquivos, ou seja, arquivos normais e alguns arquivos de dispositivo, ou terão que armazenar as informações de alguma forma (em um arquivo temporário ou memória...). É possível aqui obter todas as informações de uma vez.

Por exemplo, você poderia fazer:

$ grep -o '[<>]' < a.html | sort | uniq -c
     82 <
     82 >

POSIXamente:

fold -w 1 a.html | grep '[<>]' | sort | uniq -c

Para detectar incompatibilidade:

if fold -w 1 a.html | awk '{n[$0]++}
     END{exit(n["<"]!=n[">"])}'
then
  echo match
else
  echo mismatch
fi

Agora, para responder a pergunta do assunto, no Linux, você pode encontrar um “nome” para stdin com:

readlink -f /dev/stdin

Exemplo:

$ readlink -f /dev/stdin < a
/home/chazelas/a
$ : | readlink -f /dev/stdin
/proc/20238/fd/pipe:[758683]

(20238 acima é o pid de readlink, então esse caminho não será de muita utilidade após readlinka saída, e não seria de qualquer maneira, isso pipe:[758683]é apenas informativo, não pode seraberto).

E de forma mais geral, se lsofestiver disponível:

lsof -ad0 -p "$$" -Fn 2> /dev/null | sed -n 'n;s/^n//p'

(Porém, $$sendo o pid do processo que executou o shell, ele não funcionaria em subshells que tiveram seu stdin redirecionado)

Agora, você não será necessariamente capaz de reabrir esse arquivo para leitura e, mesmo quando o fizer, a leitura desse arquivo poderá não fornecer os mesmos dados novamente (pense em pipes, por exemplo).

$ seq 3 > a
$ { cat; cat /dev/stdin; } < a
1
2
3
1
2
3
$ cat a | { cat; cat /dev/stdin; }
1
2
3

No Linux, abrir /dev/stdinse stdin for um arquivo normal, irá ler o arquivo desde o início novamente, enquanto em outros sistemas, abrir /dev/stdin é mais parecido com um dup(0), ou seja, não retrocede o arquivo para o início (no primeiro exemplo acima , a saída seria 1\n2\n3\numa vez em vez de duas).

Responder2

Você terá que armazenar o conteúdo do arquivo de alguma forma. Você pode usar uma variável.

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

Ou um arquivo temporário (necessário se example.htmlcontiver 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

Se a leitura do conteúdo do arquivo stdin não for um requisito, você poderá passar o nome do arquivo como argumento para o 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

Chame o script assim:

$ ./myscript.sh example.html

Responder3

Uma possibilidade para sua tarefa é:

#!/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[<]}"

Se você chamar esse scriptcontagem incompatível(você pode escolher um nome mais curto), você poderá usá-lo com ou sem nome de arquivo. Algumas possibilidades:

$ countmismatched example.html
$ countmismatched < example.html
$ cat example.html | countmismatched

A saída será algo como:

> 41
< 42

Se você precisar detectar incompatibilidades, adicione no final do script:

if (( hary[<]} != hary[>] )); then
    echo "Mismatched brackets"
else
    echo "It's all good"
fi

ou algo mais 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

informação relacionada