inSANE: Um Script para automatizar (um pouco) o SANE [Shell Script]

Introdução

Como eu já relatei em um outro tópico, eu estava tendo problemas para escanear imagens. O Epson Scan (que eu usava inicialmente) parou de funcionar.

Depois fui para o Simple Scan do Gnome, que parou de funcionar direito depois que escaneei uns documentos e salvei em PDF. Não importava se eu escolhesse JPEG, PNG ou WebP, tudo o que era salvo no Simple Scan era em PDF.

Por fim, acabei encontrando o xSANE no AUR, e apesar dele ser bem feio (e um pouco confuso no começo), ele funcionou bem, mas por ser do AUR, me recomendaram remover esse pacote. E assim eu fiz.

E pesquisando uma forma de usar o SANE por linha de comando, acabei encontrando uma página na Arch Wiki explicando sobre o scanimage que escaneia imagens por linha de comando. Com isso, decidi fazer esse Script.

E uma demonstração de como esse Script pode funcionar:

Código do Script

O código do inSANE 1.0.0 está disponível no Pastebin. Basta baixar o código pelo próprio Pastebin…

… ou copiar o conteúdo, colar num editor de texto e salvar com algum nome. Se for baixar do Pastebin, renomeie para insane.sh. E nos dois casos, dê a permissão de execução.

Código do inSANE se quiser copiar e colar num editor de texto
#! /bin/bash

## Rotina para carregar as variáveis padrões do inSANE
configurarVariaveis () {
	PastaPadrao=~/Imagens/Scan ## Pasta onde o Scan será salvo
	ResolucaoPadrao=600 ## Resolução da imagem em DPI
}

## Rotina para verificar se a Pasta de Scans existe
pastaScan () {
	echo "Verificando pasta de Scans..."
	if [ -d $Pasta ]; then # Verificando se a pasta existe
		echo "Pasta de Escaneamento... OK" # Jogando a saída fora para prosseguir com o Script
	else 
		echo "Criando a pasta de Escaneamento..."
		mkdir $Pasta # Se não existe, criar a pasta
	fi
}

## Rotina para carregar as configurações
arquivoConfiguracao () {
	configurarVariaveis
	
	echo "Verificando Arquivo de Configuração..." 
	config=~/.insane.conf ## Arquivo de Configuração Padrão
	
	if [ -e "$config" ]; then ## Verificando se o Arquivo de Configuração existe
		echo "Lendo arquivo de configuração..."
		source $config ## Se existir, carrega o Arquivo de Configuração para verificar as variáveis
		
		echo -e "Verificando variáveis...\n"
		if [ -z $Pasta ]; then ## Verificando se a Variável da Pasta existe no Arquivo
			echo -e "\n	Pasta... Erro na Configuração: Esse parâmetro não está configurado\n	Usando o parâmetro padrão da Pasta..."
			Pasta=$PastaPadrao
			echo -e "	Pasta de Escaneamento:" $Pasta "\n"
		else
			echo -e "	Pasta... OK \n	Pasta de Escaneamento:" $Pasta "\n"
		fi
		
		if [ -z $Resolucao ]; then ## Verificando se a Veriável da Resolução existe no Arquivo
			echo -e "	Resolução... Erro na Configuração: Esse parâmetro não está configurado\n	Usando parâmetro padrão da Resolução..."
			Resolucao=$ResolucaoPadrao
			echo  -e "	Resolução:" $Resolucao "\n"
		else
			echo  -e "	Resolução... OK\n	Resolução:" $Resolucao "\n"
		fi
		
		pastaScan ## Verificar se a pasta do Escaneamento existe
		
	else
		echo "Usando configurações padrões..."
		Pasta=$PastaPadrao
		Resolucao=$ResolucaoPadrao
		echo -e  "	Pasta de Escaneamento:" $Pasta "\n	Resolução:" $Resolucao "\n"
		pastaScan
	fi
}

selecionarScanner () {
	scanimage -L | grep -v Camera | gawk -F '`' -P '{ print $2 }' | cut -d"'" -f1 ## Primeiro indentifica os Scanners, Depois retira a Camera Integrada e por fim extrai o ID do dispositivo,
}

## Rotina para verificar se o Zenity está instalado para exibir uma barra de progresso. O Zenity é uma dependência opcional
progresso () {
	if [ -z `command -v zenity` ]; then ## Verificando se o Zenity está instalado
		echo "" > /dev/null ## Se o Zenity não estiver instalado, prossiga
	else
		zenity --progress --title="inSANE" --pulsate --auto-close --no-cancel --text="Escaneando..." ## Se o Zenity estiver instalado, exiba uma caixa de diálogo com uma barra de progresso
	fi
}

echo -e "\nBem-vindo ao inSANE\nEsse é um Script simples para usar o Scanner por meio do SANE\nVersão 1.0.0\nScript desenvolvido por Rapoelho\n"
arquivoConfiguracao

echo "Detectando Scanner..."
Scanner=`selecionarScanner` ## Selecionando o Scanner

echo "Foi Detectado o Scanner" $Scanner"."
echo "Escaneando..."
scanimage --device "$Scanner" --format=jpeg --output-file $Pasta/Scan_`date +"%Y-%m-%d_%H-%M-%S"`.jpg --resolution $Resolucao | progresso

if [ $? -eq 0 ]; then
    echo "Imagem escaneada com sucesso em" $Pasta "em" $Resolucao "DPI"
fi

Como Funciona

O inSANE funciona de uma forma bem simples: Ele detecta o seu Scanner, e usa o scanimage para escanear a imagem na resolução de 600 DPI e na pasta ~/Imagens/Scan.

E claro, tem como configurar esses dois parâmetros criando um arquivo .insane.conf que o inSANE procura antes de usar os valores padrões, com o arquivo tendo as variáveis da Pasta e da Resolução, sendo mais ou menos assim:

Pasta=~/Imagens/Escaneados
Resolucao=300

Como funciona a detecção automática do Scanner?

Essa pra mim foi uma das partes mais interessantes desse Script e por isso decidi comentar ela. Para o scanimage funcionar, ele precisa ter o dispositivo, o formato do arquivo, o arquivo de saída. Na Arch Wiki, o exemplo que tem é esse aqui:

scanimage --device "pixma:04A91749_247936" --format=tiff --output-file test.tiff --progress

Então a forma para detectar automaticamente o Scanner foi essa aqui:

scanimage -L | grep -v Camera | gawk -F '`' -P '{ print $2 }' | cut -d"'" -f1

E porque esse comando para poder extrair um ID de dispositivo? Começando pelo primeiro comando, o scanimage -L ele existe para que seja feita uma lista de quais dispositivos existem. E a saída dele acaba sendo essa aqui:

Como observado, só preciso do Scanner, no caso a minha Epson L3150, e não da câmera integrada. Com isso, vem o segundo comando, o grep -v Camera para poder retirar o “Noname Integrated Camera” do caminho.


Com isso, a linha do Scanner foi isolada. Mas ainda é preciso limpar bastante essa linha, pois preciso apenas do epsonds:libusb:001:091. E nisso, vem o terceiro comando, o…

gawk -F '`' -P '{ print $2 }'

… para retirar o device do começo da linha. Com isso, a saída fica assim:

E por fim, tem que limpar o restante da linha. Com isso, vem o cut -d"'" -f1, que faz esse serviço de cortar tudo o que há depois do '. E por fim, a saída se torna assim, que é o que quero para esse Script.

E isso foi necessário, pois toda vez que eu desconecto e reconecto o Hub USB em que está o Scanner, o ID dele muda. E pode ser que essa Detecção não funcione para todos e que precise de ajustes.

Detecção do Arquivo de Configuração

Esse Script foi feito se ajustando as minhas necessidades, que é salvar o arquivo na pasta ~/Imagens/Scan e com uma resolução de 600 DPI, mas isso não quer dizer que ele não seja configurável.

Basicamente há uma rotina que verifica se o arquivo .insane.conf existe na pasta pessoal do usuário e se as variáveis existem.

Infelizmente não consegui uma forma de ver se essas variáveis são válidas, o que pode causar erros se as variáveis existirem no arquivo, mas não forem adequadas para o scanimage

E claro, se o arquivo de configuração e/ou alguma variável não existir, o inSANE passará os valores padrão para o scanimage e assim fazer o escaneamento do arquivo.

Detecção da Pasta de Escaneamento

Outra coisa que o inSANE também faz é verificar se a pasta em que os arquivos serão escaneados existe. Se a pasta existir, o escaneamento será feito normalmente e se não existir, o próprio inSANE cria a pasta. Seja a pasta padrão (~/Imagens/Scan), seja a pasta configurada com a variável Pasta= no .insane.conf.

O escaneamento em si

E por fim, o escaneamento. Com todas as variáveis carregadas e configuradas, é hora de usar o scanimage

scanimage --device "$Scanner" --format=jpeg --output-file $Pasta/Scan_`date +"%Y-%m-%d_%H-%M-%S"`.jpg --resolution $Resolucao

A variável $Scanner é obtida por meio da rotina de detecção do Scanner, o formato do Arquivo é o JPEG, que dentre os formatos que o meu Scanner suporta é o mais “leve”.

A variável $Resolução é obtida com a rotina que detecta o arquivo de configuração. E por fim, uma das coisas mais legais desse comando:

--output-file $Pasta/Scan_`date +"%Y-%m-%d_%H-%M-%S"`.jpg

Essa é a variável do Arquivo de saída. Independentemente de onde o Script for executado, ele salvará ou na pasta padrão ou na pasta que está configurada no .insane.conf. E uma coisa é que esse Script sempre salvará os arquivos com o prefixo Scan_ e com a data e hora em que o escaneamento começou.

Optei por isso, pois essa era a forma em que o XnViewMP escaneava os arquivos do Windows: Salvando com o prefixo Scan_ junto com a Data e Hora em que o escaneamento foi feito. É uma forma de salvar o arquivo sem pensar muito no nome dele e de manter os arquivos escaneados organizados.

E um outro detalhe: Se o Zenity estiver instalado, o inSANE vai exibir um diálogo com uma barra de progresso para mostrar que o escaneamento está acontecendo.

Problemas conhecidos

Um bug que me deparei com esse código é que às vezes o scanimage -L não consegue detectar o scanner. A solução pra isso é desconectar e conectar o cabo USB do Scanner.

E o que pode ser um problema: Até onde testei, o inSANE funciona apenas com Scanners conectados via USB. Não consegui testar com nada via Rede.

Palavras Finais

Assim como a experiência de fazer aquele Script para a Bateria, essa foi uma experiência que gostei bastante e que decidi compartilhar com vocês. Sei que tem muita coisa que pode ser melhorada, mas para uma primeira versão desse Script estou bem satisfeito.

Talvez eu possa melhorar esse Script com a adição de opções para digitalizar arquivos em PDF, ou talvez com outros formatos de imagem. Ou mesmo quem sabe, com a opção de rotacionar o arquivo digitalizado (e que pode ser muito útil).

Enfim, esse foi um Script que fiz seguindo as minhas necessidades, mas que ajustei ele um pouco para atender as necessidades de algumas outras pessoas.

7 curtidas

Nossa que top, se adicionasse uma GUI tmb ia ser legal, porque o pessoal se amarra em GUI.

Rapaz tem uma barra de progresso, legal d+.

3 curtidas

Apenas não adicionei uma interface mais trabalhada nele, pois fiz ele para ser bem simples, do tipo “Só escaneie isso em 600 DPI e salve na minha pasta de Scans”.

E a barra de progresso foi mais para ter algo visual mesmo de que a digitalização está ocorrendo, apesar de ter feito o Script de uma forma que ele funcione mesmo sem o Zenity instalado.

3 curtidas

Já usou o scanner da Epson L3150 pela rede sem o cabo USB via scanimage?

A Epson identifica o scanner pela rede wifi só não digitalizar já pelo cabo funciona 100%.

3 curtidas

Funciona apenas pelo Epson Scan 2. Mas o Epson Scan parou de funcionar.
Ele só estava fechando antes de salvar as imagens e com isso, tive que buscar alternativas.

1 curtida

Parece algo proprietário a comunicação do scanner via rede wifi.

1 curtida

No Epson Scan para Linux, a configuração fica bem explicita. Tipo, é uma das primeiras coisas que você faz é adicionar o endereço do Scanner, caso ele não esteja conectado via USB.

E no Simple Scan, que eu estava usando antes de usar o scanimage, ele só reconhecia o Scanner via USB.

1 curtida

Mesmo caso do xsane :rofl:

1 curtida

Também acabei postando o inSANE no Github. Esse foi o meu primeiro código que postei por lá.

2 curtidas

Qual seria o conteúdo do arquivo de configuração ~/.insane.conf?

2 curtidas

Esse:

Pasta=~/Imagens/Escaneados
Resolucao=300
1 curtida

Atualizado…

Problema de não identificar o dispositivo após o uso (scanimage).

Tira e coloca novamente o cabo usb.

1 curtida

Mesmo que eu tenha reinstalado o sistema no começo do ano, eu ainda utilizo muito o inSANE. Acho que esse foi o Script mais útil que eu já fiz. E ele funciona normalmente sem ter o Zenity instalado.

Talvez, eu coloque uma notificação de que o escaneamento terminou utilizando o notify-send.

E a versão mais recente (a 1.0.2) foi nada mais do que uma reorganização do código usando o ShellCheck, que deu algumas dicas de como fazer o código funcionar sem erros (embora eu não tenha enfrentado nenhum erro antes).

1 curtida

@rapoelho Shellcheck faz mais do que mostrar erros, ele tmb da dicas de boa pratica, que são as reclamações em verdes.
Existe ferramentas de checagem de varias linguagens de programação, e o shell não podia ficar de fora, ai temos o shellcheck para nos auxiliar.

1 curtida

Script atualizado e verificado pelo shellcheck

O problema não é o script e sim o comando scanimage que pode deixar de reconhece o scanner ai só tirando e colocando o cabo USB no computador para voltar a reconhecer.

Falso positivo do shellcheck

O erro SC1091 indica que o comando source "$config" está tentando carregar um arquivo de configuração ($HOME/.insane.conf), mas esse arquivo não foi passado como argumento para o shellcheck. O shellcheck não consegue verificar se o arquivo realmente existe ou se é carregado corretamente porque o arquivo não foi especificado no momento da execução da análise.

2 curtidas

Esse problema de falso positivo do shellcheck não tem como resolver na unha, isso porque ele não tem como atualmente de verificar os arquivos carregados com o source, sempre que usar o source vai dar algum problema, uma forma de mitigar isso é não usar o source, embutindo o arquivo do source dentro do código do script.

1 curtida

Inclusive o shellcheck esta avisando que ele não consegue seguir source, veja “not following:”.

2 curtidas

Atualizado o script (funcionalidades desativadas)

check_saned_status (usuario comum deve esta no grupo sudo)
check_sudo (usuario comum deve esta no grupo sudo)
verificar_scanner usando o lsusb (pode ter falso positivo)

Se usar # em função tem falso positivo também no shellcheck.

O shellcheck podia reclamar da falta de function no inicio da função… :rofl: :rofl: :rofl: :rofl:

2 curtidas

Essas duas funções seriam por qual motivo?

1 curtida

Isso é gordura do Bash, faz nenhuma diferença, e se faz nunca vi a diferença na pratica, nem o a comunidade que programam o shellcheck deve saber para que serve function.