Como escrever um script que aceita entrada de um arquivo ou de stdin?

48

Como alguém pode escrever um script que aceite entrada de um argumento de nome de arquivo ou de stdin?

por exemplo, você pode usar less dessa maneira. pode-se executar less filename e equivalentemente cat filename | less .

Existe uma maneira fácil de usar? ou preciso reinventar a roda e escrever um pouco de lógica no script?

    
por gilad hoch 30.04.2014 / 11:46

8 respostas

51

Se o argumento file for o primeiro argumento do seu script, teste se existe um argumento ( $1 ) e se é um arquivo. Outra leitura de entrada de stdin -

Assim, seu script pode conter algo assim:

#!/bin/bash
[ $# -ge 1 -a -f "$1" ] && input="$1" || input="-"
cat $input

por exemplo. então você pode chamar o script como

./myscript.sh filename

ou

who | ./myscript.sh

Editar Alguma explicação do roteiro:

[ $# -ge 1 -a -f "$1" ] - Se pelo menos um argumento de linha de comando ( $# -ge 1 ) AND (-a operador) o primeiro argumento for um arquivo (-f testes se "$ 1" for um arquivo) então o resultado do teste é verdadeiro.

&& é o operador AND lógico do shell. Se o teste for verdadeiro, atribua input="$1" e cat $input como saída do arquivo.

|| é o operador OR lógico do shell. Se o teste for falso, os comandos que seguem || serão analisados. entrada é atribuída a "-". cat - é lido no teclado.

Resumindo, se o argumento do script for fornecido e for um arquivo, a entrada da variável será atribuída ao nome do arquivo. Se não houver um argumento válido, cat lê do teclado.

    
por 30.04.2014 / 12:46
12

read lê a entrada padrão. Redirecioná-lo do arquivo ( ./script <someinput ) ou através do pipe ( dosomething | ./script ) não fará com que funcione de maneira diferente.

Tudo o que você precisa fazer é percorrer todas as linhas na entrada (e não difere de iterar sobre as linhas no arquivo).

(código de amostra, processa apenas uma linha)

#!/bin/bash

read var
echo $var

Irá ecoar a primeira linha da sua entrada padrão (através de < ou | ).

    
por 30.04.2014 / 12:07
4

Você não menciona qual shell você planeja usar, então eu vou assumir o bash, embora estas sejam coisas bem padronizadas em shells.

Argumentos de arquivo

Argumentos podem ser acessados através das variáveis $1 - $n ( $0 retorna o comando usado para executar o programa). Digamos que eu tenha um script que apenas cat s encontre um número de arquivos com um delimitador entre eles:

#!/usr/bin/env bash
#
# Parameters:
#    1:   string delimiter between arguments 2-n
#    2-n: file(s) to cat out
for arg in ${@:2} # $@ is the array of arguments, ${@:2} slices it starting at 2.
do
   cat $arg
   echo $1
done

Neste caso, estamos passando um nome de arquivo para cat. No entanto, se você quisesse transformar os dados no arquivo (sem explicitamente escrever e reescrevê-los), você também poderia armazenar o conteúdo do arquivo em uma variável:

file_contents=$(cat $filename)
[...do some stuff...]
echo $file_contents >> $new_filename

Ler de stdin

No que diz respeito à leitura de stdin, a maioria dos shells tem um padrão bastante natural de read , embora existam diferenças em como os prompts são especificados (no mínimo).

A página do manual do Bash builtins tem uma explicação bastante concisa de read , mas eu prefiro a página Bash Hackers .

Simplesmente:

read var_name

Múltiplas Variáveis

Para definir várias variáveis, basta fornecer vários nomes de parâmetros para read :

read var1 var2 var3

read colocará uma palavra de stdin em cada variável, colocando todas as palavras restantes na última variável.

λ read var1 var2 var3
thing1 thing2 thing3 thing4 thing5
λ echo $var1; echo $var2; echo $var3
thing1
thing2
thing3 thing4 thing5

Se menos palavras forem inseridas do que variáveis, as variáveis restantes permanecerão vazias (mesmo se previamente definidas):

λ read var1 var2 var3
thing1 thing2
λ echo $var1; echo $var2; echo $var3
thing1
thing2
# Empty line

Prompts

Eu uso -p flag frequentemente para um prompt:

read -p "Enter filename: " filename

Nota: ZSH e KSH (e talvez outros) usam uma sintaxe diferente para prompts:

read "filename?Enter filename: " # Everything following the '?' is the prompt

Valores padrão

Este não é realmente um truque de read , mas eu uso muito em conjunto com read . Por exemplo:

read -p "Y/[N]: " reply
reply=${reply:-N}

Basicamente, se a variável (resposta) existir, retorne a si mesma, mas se ela estiver vazia, retorne o seguinte parâmetro ("N").

    
por 30.04.2014 / 14:21
4

A maneira mais simples é redirecionar você mesmo stdin:

if [ "$1" ] ; then exec < "$1" ; fi

Ou se preferir a forma mais concisa:

test "$1" && exec < "$1"

Agora, o resto do seu script pode ler apenas de stdin. É claro que você pode fazer da mesma forma com a análise de opções mais avançada em vez de codificar a posição do nome do arquivo como "$1" .

    
por 30.04.2014 / 21:23
3

use (ou encadeie) outra coisa que já se comporte dessa maneira, e use "$@"

digamos que eu queira escrever uma ferramenta que substitua as execuções de espaços no texto por guias

tr é a maneira mais óbvia de fazer isso, mas somente aceita stdin, então temos que separar cat :

$ cat entab1.sh
#!/bin/sh

cat "$@"|tr -s ' ' '\t'
$ cat entab1.sh|./entab1.sh
#!/bin/sh

cat     "$@"|tr -s      '       '       '\t'
$ ./entab1.sh entab1.sh
#!/bin/sh

cat     "$@"|tr -s      '       '       '\t'
$ 

para um exemplo em que a ferramenta que está sendo usada já se comporta dessa maneira, poderíamos reimplementar isso com sed :

$ cat entab2.sh
#!/bin/sh

sed -r 's/ +/\t/g' "$@"
$ 
    
por 30.04.2014 / 19:23
2

Você também pode fazer:

#!/usr/bin/env bash

# Set variable input_file to either $1 or /dev/stdin, in case $1 is empty
# Note that this assumes that you are expecting the file name to operate on on $1
input_file="${1:-/dev/stdin}"

# You can now use "$input_file" as your file to operate on
cat "$input_file"

Para obter mais truques de substituição de parâmetros no Bash, consulte este .

    
por 05.09.2014 / 21:41
0

Você também pode mantê-lo simples e usar este código

Quando você cria um arquivo de script pass_it_on.sh com este código,

#!/bin/bash

cat

Você pode executar

cat SOMEFILE.txt | ./pass_it_on.sh

e todo o conteúdo do stdin será simplesmente expelido para a tela.

Como alternativa, use este código para manter uma cópia de stdin em um arquivo e depois exibi-la na tela.

#!/bin/bash

tmpFile='mktemp'
cat > $tmpFile
cat $tmpFile    

e aqui está outro exemplo, talvez mais legível, explicado aqui:

link

#!/bin/bash

VALUE=$(cat)

echo "$VALUE"

Divirta-se.

RaamEE

    
por 13.03.2015 / 20:14
0

A maneira mais simples e compatível com POSIX é:

file=${1--}

que é equivalente a ${1:--} .

Em seguida, leia o arquivo como de costume:

while IFS= read -r line; do
  printf '%s\n' "$line" # Or: env POSIXLY_CORRECT=1 echo "$line"
done < <(cat -- "$file")
    
por 29.05.2015 / 19:10