Eu tenho o seguinte código que recebe um arquivo por meio de um argumento e, se não existir, pegue-o do stdin:
file=
column=
pattern=
path=
cmd="echo"
while getopts "f:c:e:" opt; do
case $opt in
f) file="$OPTARG";;
c) column="$OPTARG";;
e) pattern="$OPTARG";;
*) echo "Usage: $0 -f [file] -c [column] -e [pattern]"
esac
done
if [ -z "$file" ]; then
file=$(cat)
fi
o problema com essa abordagem é que cat não funcionará em arquivos que não sejam, portanto, se for obtido do stdout, por exemplo:
cat data.csv | ./read.sh -c 2 -e " Max"
então não poderei executar:
cat "$file"
dentro do script bash, eu teria que usar echo.
OTOH, se for um arquivo, posso usar cat e está tudo bem. Minha pergunta é como fazer com que o script seja capaz de reconhecer se o var $file é ou não um nome de arquivo real ou stdout.
Editado: levei em consideração os comentários e respostas, aqui está todo o meu script de como leio para o usuário a coluna e o regex especificados e considero ambos passando um arquivo do stdin ou como um argumento:
#!/bin/bash
unset -v column pattern filepath
cmd=echo
DELIMITER=","
while getopts "f:c:e:" opt; do
case $opt in
f) file="$OPTARG";;
c) column="$OPTARG";;
e) pattern="$OPTARG";;
*) printf>&2 '%s\n' "Usag: $0 [-f file] [-c column] [-e pattern]"; exit 1
esac
done
if [ -z "$file" ]; then
file=$(cat)
elif [ -z "$column" ]; then
echo "column number neeeded!"
elif [ -z "$pattern" ]; then
echo "pattern needed!"
fi
if [ -f "$file" ]; then
cmd="cat"
fi
if [ "$column" -eq 5 ]; then
DELIMITER=":"
fi
"$cmd" "$file" | awk -v col="$column" -v pattern="$pattern" -v del="$DELIMITER" -F "$DELIMITER" '{
for (i=1;i<=NF;i++) {
if ( i == col && $i == pattern ) {
print $0
} else if ( del == ":" && $i == pattern ) {
print $0
}
}
}'
exit 0
funciona, mas tenho certeza de que não é a abordagem correta. mais uma vez, estou tentando deixar meu arquivo fazer as duas coisas:
cat data.csv | ./read.sh -c 2 -e "Max"
e:
./read.sh -f data.csv -c 2 -e "Max"
o script acima funciona, mas a otimização é o nome do jogo!
Para responder à sua pergunta real, você pode usar
test
para ver se é um arquivo. entãoNo entanto, acho que isso está faltando, você entendeu
file=$(cat)
e acho que está confuso com a reutilização dofile
nome da variável. Se, em vez disso, você dissesse,file_contents=$(cat)
acho que achará as coisas mais claras.Pois
cat
,-
significa stdin. Em muitos sistemas/dev/stdin
funcionaria com qualquer comando, não apenascat
, enquanto no Linux ou Cygwin,/dev/stdin
apontaria para o mesmo arquivo aberto no stdin, não se comportando exatamente da mesma forma que stdin em vários casos.Então você poderia fazer:
Cuidado, você poderá ler stdin (
-
) apenas uma vez.Observe também:
echo
não pode ser usado para dados arbitrários (como$0
sobre os quais você não tem controle).[...]
em mensagens de uso por convenção significa opcional , então com-f [file]
você está sugerindo-f
que é obrigatório, mas seu argumento é opcional, o que não é verdade aqui.path
se esse script acabar sendo interpretadozsh
(quando não estiver emsh
emulação), onde$path
está a variável da matriz vinculada$PATH
como emcsh
/tcsh
.-f ''
, você obteria um valor vazio$file
, mesmo que o usuário passasse explicitamente um argumento para-f
(reconhecidamente um argumento falso para um caminho de arquivo aqui). Em vez disso, useunset -v var
na inicialização para garantir que a variável esteja desativada e verifique[ -z "${var+set}" ]
se ela ainda está desativada posteriormente. Ou use variáveis de sinalização booleanas extras para registrar se a opção foi aprovada ou não.-
e signifique o arquivo real chamado-
no diretório de trabalho atual, não no stdin, você o transformaria./-
na parte que manipula(f)
.file=$(cat)
lê o que pode ser lido no stdin e armazena nafile
variável (na memória). Então, como outros já disseram, o nome da variável é um pouco enganador, pois é o conteúdo do arquivo de entrada que você obtém. Mas também é o conteúdo do mangle que você obtém, pois$(...)
remove todos os caracteres de nova linha finais e, exceto em zsh, remove ou engasga os caracteres NUL. Se você usarecho
(que novamente não pode ser usado para dados arbitrários) nele, eleecho
fará sua própria confusão.Aqui você também pode adotar a abordagem inversa e abrir o arquivo passado como argumento no
-f
stdin:Observe que os shells POSIX sairiam automaticamente quando
exec
o arquivo não fosse aberto. Se for passada mais de uma-f
opção, como na atribuição$file
, apenas a última será levada em consideração.