inSANE - Escaneando Coisas de uma forma Preguiçosa
07 Nov 2025 (updated: 07 Nov 2025 )
Era Novembro de 2024, e fazia menos de um mês que eu tinha migrado para o Linux, até que aconteceu um problema: O Epson Scan 2, que é o programa que eu usava para usar o Scanner da minha impressora, parou de funcionar.
Depois fui para uma solução do próprio Gnome: O Simple Scan. Funcionou muito bem, até escanear um Arquivo em PDF. E o Simple Scan passou a salvar tudo como um PDF, o que pra mim era inútil.
Depois tentei o xSANE via AUR. Ele é horroroso, mas funcionou bem. Porém, como eu tenho uma política de “AUR apenas em último caso”, acabei desinstalando ele.
E pesquisando uma alternativa, acabei encontrando uma página na Arch Wiki explicando sobre o scanimage, que funciona por linha de comando e como sou um preguiçoso, decidi fazer um Script.
scanimage: O Coração do Script
O comando do scanimage é bem simples, sendo assim.
scanimage --device "pixma:04A91749_247936" --format=tiff --output-file test.tiff
E que tem os seguintes argumentos:
-- device: Aqui é o dispositivo que será usado para fazer o escaneamento;-- format: Aqui é o formato do arquivo do escaneamento;-- output-file: Aqui vai o nome do arquivo de saída.
Bem simples, né? Só colocar o ID do dispositivo, o formato da imagem e o arquivo de saída.
Obtendo o ID do Dispositivo
Mas, como obter o ID do dispositivo? Nesse caso, é usado o comando scanimage -L, que retorna os IDs dos dispositivos que foram encontrados.

Nesse caso, é só pegar o ID do Scanner; no caso acima, o `epsonds:libusb:001:023'; e usar o comando, certo? Bom, isso funciona até certo ponto. Quando o Scanner é desconectado e reconectado, o ID muda.

Para resolver isso, inicialmente, usei esse comando aqui:
scanimage -L | grep -v Camera | awk -F '`' -P '{ print $2 }' | cut -d"'" -f1
Mas o que ele faz? Como demonstrado, o scanimage -L detecta os dispositivos que podem ser usados com o SANE. Mas, aparece um dispositivo de câmera, não quero ter isso. Quero usar o Scanner.
Com isso, vem o | grep -v Camera que redireciona a lista que foi gerada para o grep, que basicamente filtra palavras e expressões da saída de um comando. E no grep, foi usado o -v, que retira da saída do comando, as linhas que contém a palavra ou a frase que estou procurando.

Porém, isso não é o suficiente. Essa saída ainda precisa de polimentos. Com isso, vem mais um comando: o | awk -F '`' -P '{ print $2 }'. Esse comando, basicamente ele retira tudo o que está antes do `. Nesse caso, o device `.

Isso não é o suficiente. Quero apenas o ID. o “is a Epson L3150 Series ESC/I-2” é completamente inútil no comando e isso tem que ser retirado. Com isso, vem o último comando dessa cadeia, o | cut -d"'" -f1. Basicamente, esse comando corta tudo o que está depois do ', deixando apenas o que é necessário.

E com isso, temos o ID do Scanner.
Melhorando a Detecção do Scanner
Porém, ao testar em algumas distribuições Linux, tive problemas, principalmente no Linux Mint, que configura a minha Epson como um Scanner de Rede.
Tive que arrumar um jeito para fazer o inSANE funcionar exclusivamente com Scanners USB. Com isso, recorri a outro comando:
scannerID=$(sane-find-scanner | grep possible | awk '{print $NF}')
E o que esse comando faz? Ele usa o sane-find-scanner para detectar possíveis Scanners conectados nas portas USB do computador. A saída desse comando, é essa aqui:

Basicamente ele pesquisa por todas as portas USB, e tenta ver se aquilo é um Scanner. Ao menos, é assim que parece que ele funciona. Com isso, vem outro comando para poder filtrar isso melhor: o | grep possible. Esse comando irá filtrar pelas linhas que estão marcadas como possible. O que diminui muito a saída.

E por fim, é usado o comando | awk '{print $NF}' para poder extrair apenas o ID da USB. Esse awk extrai apenas a última coisa que há na linha.

Porém esse ID está incompleto. Com isso, ele é guardado na variável scannerID e essa variável é usada em outro lugar: Naquele comando em que era utilizado anteriormente para poder detectar o Scanner. Ou seja, ao invés do comando ficar assim…
scanimage -L | grep -v Camera | awk -F '`' -P '{ print $2 }' | cut -d"'" -f1
… ele fica assim.
scanimage -L | grep $scannerID | awk -F '`' '{ print $2 }' | cut -d"'" -f1
Agora, o inSANE está funcionando exclusivamente com Scanners USB. Mas, pode ser que eu tenha que melhorar mais essa parte do Script.
Configurando as Variáveis Padrão do Script
A primeira coisa que foi necessário fazer no Script, foi definir algumas variáveis, como a Pasta Padrão, a Pasta de Imagens e a Resolução Padrão.
configurarVariaveis () {
PastaImagens=$(xdg-user-dir PICTURES)
PastaPadrao=$PastaImagens/Scan ## Pasta onde o Scan será salvo
ResolucaoPadrao=600 ## Resolução da imagem em DPI
}
Optei por separar a variável da Pasta Padrão em duas: Uma para detectar a pasta de imagens do Usuário com o comando xdg-user-dir PICTURES e a outra com o resultado da variável anterior com a adição da pasta Scan.
Talvez isso não tenha sido necessário, mas optei por fazer para que, pra mim, o Script ficasse mais fácil de entender.
Já a Resolução, escolhi 600 DPI, pois é a Resolução que acho mais adequada na hora de fazer a Linework do desenho, pois não preciso ficar dando Zoom na imagem. Um 100% de Zoom, já basta em muitos dos casos.
Lendo Arquivos de Configuração
Mas e se eu precisar escanear em uma pasta diferente com uma resolução menor? É para isso que implementei um suporte a arquivos de configuração no inSANE. Basta criar um arquivo .insane.conf na sua pasta pessoal nesse modelo…
Pasta=~/Imagens/Escaneados
Resolucao=300
… que tudo funciona, conforme o que foi colocado no .insane.conf. Mas como isso funciona? É a rotina arquivoConfiguracao que faz isso.
configurarVariaveis
echo "Verificando Arquivo de Configuração..."
config=~/.insane.conf ## Arquivo de Configuração Padrão
Primeiramente, ela carrega a função carregarVariaveis, que define as variáveis padrão da Pasta e Resolução. Isso serve como um Backup, no caso de alguma das variáveis não existir no Arquivo e aqui é definido o nome do Arquivo de configuração.
Depois, é feito um teste para ver se o Arquivo existe.
if [ -e "$config" ]; then
E se o Arquivo existir, ele é carregado com o comando source…
echo "Lendo arquivo de configuração..."
source $config
echo -e "Verificando variáveis...\n"
… e logo as variáveis começam a ser verificadas, começando pela variável da Pasta onde os Arquivos Escaneados irão e depois, indo para a Resolução.
if [ -z "$Pasta" ]; then
echo -e "\nPasta... Erro na Configuração: Esse parâmetro não está configurado\nUsando o parâmetro padrão da Pasta..."
Pasta=$PastaPadrao
echo -e " Pasta de Escaneamento:" "$Pasta" "\n"
else
echo -e " Pasta... OK \nPasta de Escaneamento:" "$Pasta" "\n"
fi
if [ -z "$Resolucao" ]; then
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
Como o source carrega o arquivo de configuração, as Variáveis já estão carregadas. Então, o que o if [ -z ] verifica é se a variável está vazia. Se elas não estiverem vazias, isso significa que elas vieram do Arquivo de Configuração, então são elas que serão usadas.
E se elas estiverem vazias, isso significa que elas são estão carregadas, e com isso as variáveis padrão serão carregadas. Mas e se o arquivo de configuração não existir? Nesse caso, é executado esse bloco de comandos, onde as Variáveis são definidas como as padrões.
echo "Usando configurações padrões..."
Pasta=$PastaPadrao
Resolucao=$ResolucaoPadrao
echo -e " Pasta de Escaneamento:" "$Pasta" "\n Resolução:" $Resolucao "\n"
pastaScan
E nos dois casos, o inSANE irá chamar outra função para verificar se a pasta de Scans existe. E é um código bem simples pra isso.
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
}
Primeiramente, o inSANE verifica se a pasta existe. Se ela existir, ele prossegue, e se não existir, a pasta é criada automaticamente.
Yay! Notificações
Isso foi algo que eu quis incrementar no inSANE, principalmente para saber se o Escaneamento começou, qual Scanner foi detectado e quando terminou. Eu poderia apenas executar o inSANE por linha de comando, mas tem aqueles momentos que eu não quero isso.
E como fazer isso? Usei o mesmo if [ -z para verificar se o notify-send existe. Só que, ao invés de usar uma variável, estou usando o comando command -v, que verifica se um determinado comando existe no sistema.
if [ -z "$(command -v notify-send)" ]; then
E se o comando existe, ele irá retornar o caminho do comando e se não existir, ele vai retornar uma saída em branco.
![]() |
![]() |
|---|
Tive que fazer isso, pois em algumas distros esse comando não existe por padrão. E queria ter uma forma do Script funcionar sem isso. Ou seja, se o comando não existir, o Script apenas irá prosseguir, jogando a saída para o /dev/null.
echo "" > /dev/null
Tipos de Notificação
Mas se o comando existir, começa uma série de if e elif para verificar qual foi a entrada que foi dada para essa função, pois fiz ela para ter algumas variantes. E as variantes são:
- Detecção do Scanner:
detectandoScanner; - Scanner Detectado:
scannerDetectado; - Escaneamento em Progresso:
escaneando; - Escaneamento Finalizado:
escaneamentoConcluido; - SANE não Encontrado:
faltaSANE; - Scanner não Encontrado:
erroScanner.
Detecção do Scanner
Se a função receber a entrada de que a Detecção do Scanner está ocorrendo, ela invoca esse comando…
notify-send -a "inSANE" -i scanner "Aguarde..." "Detectando Scanner..."
… que dá nessa notificação.

Scanner Detectado
Se a função receber a entrada de que o Scanner foi detectado, ela invoca esse comando…
notify-send -a "inSANE" -i scanner "Scanner Detectado!" "Foi detectado o scanner $Scanner"
… o que dá nessa notificação, que informa que o Scanner foi detectado e utiliza o ID do Scanner para mostrar qual está sendo usado.

Escaneamento em Progresso
Se a função receber a entrada de que o Escaneamento está em Progresso, ela invoca esse comando…
notify-send -a "inSANE" -i scanner "Aguarde..." "Escaneando a imagem em $Resolucao DPI na pasta $Pasta"
… que dá nessa notificação, onde informa o local em que a imagem está sendo escaneada e em qual resolução.

Escaneamento Finalizado
Para a entrada “Escaneamento Finalizado”, a coisa é um pouco diferente. O que é invocado dessa vez, é esse bloco de comando…
AbrirImagem=$(notify-send -a "inSANE" "Escaneamento Concluído!" "Imagem escaneada com sucesso!" -i image --action="Abrir a Imagem Escaneada" -u critical) > /dev/null
case $AbrirImagem in # Se clicar no botão para Abrir a Imagem na Notificação...
"0")
xdg-open "$Arquivo"
;;
esac
… o que dá nessa notificação

Como eu quis que esse fosse uma notificação que colocasse uma opção para abrir a imagem no Visualizador de Imagens padrão, foi daquela forma que tive que fazer.
Porém, dependendo do sistema de notificações que está sendo utilizado pelo Ambiente Gráfico, a opção de Abrir a Imagem com o Visualizador de Imagens, pode não funcionar ou sequer aparecer, como no caso do dusnt.
SANE não Encontrado!
Sim, também tem notificações para o caso do SANE não ser encontrado. Nesse caso, quando a função recebe a entrada de “SANE não Encontrado”, é invocado esse comando…
notify-send -a "inSANE" -i scanner "Erro! SANE não encontrado!" "O SANE não está instalado. Favor, instalar o SANE."
… que exibe essa notificação.

Scanner não Encontrado
E por fim, se a função receber a entrada de que o Scanner não foi entrado, é invocado esse comando…
notify-send -a "inSANE" -i scanner "Erro! Scanner não encontrado!" "Conecte um Scanner compatível na porta USB ou desconecte e reconecte o Scanner."
… que exibe essa notificação.

Detectando se o SANE está instalado
Em algumas das distros que testei e em outras que eu usei, percebi que o SANE não vem instalado. Com isso, tive a ideia de implementar uma função para verificar se o SANE está instalado. E essa função utiliza a mesma base da que verifica se o notify-send está instalado.
verificarSANE () {
if [ -z "$(command -v scanimage)" ]; then ## Verificando se o SANE está instalado
echo "O SANE não está instalado. Favor, instalar o SANE antes de prosseguir."
notificacoes faltaSANE
else
echo "" > /dev/null ## Se o SANE estiver instalado, prossiga
fi
}
Basicamente, se o SANE não estiver instalado, ela envia uma mensagem de erro no Terminal e uma notificação, informando que falta instalar o SANE. E se estiver instalado, o inSANE prossegue.
Por fim, o Escaneamento!
Depois disso tudo, finalmente vem os últimos comando, que junta tudo para poder escanear as imagens. Primeiramente, uma mensagem de boas vindas…
echo -e "\nBem-vindo ao inSANE\nEsse é um Script simples para usar o Scanner por meio do SANE\nVersão 1.1.0\nScript desenvolvido por Rapoelho\n"
… depois disso, a função que verifica se o SANE está instalado e a que carrega o Arquivo de Configuração e as Variáveis.
verificarSANE
arquivoConfiguracao
echo "Detectando Scanner..."
notificacoes detectandoScanner
Scanner=$(selecionarScanner)
E a Detecção do Scanner dá início, com a variável do Scanner sendo alimentada com a função que detecta o Scanner e a notificação de que a Detecção do Scanner está ocorrendo.
Com isso, o inSANE passa para o próximo passo: Verificar a variável do Scanner.
if [ "$Scanner" == "" ]; then
echo -e "\nScanner não encontrado!\nPossíveis Soluções:\n - Conecte um Scanner na porta USB do Computador\n - Desconecte e Conecte o Scanner.\n - Conecte um Scanner compatível com o SANE"
notificacoes erroScanner
exit
else
echo "Foi Detectado o Scanner" "$Scanner""."
notificacoes scannerDetectado
fi
echo "Escaneando..."
notificacoes escaneando
Basicamente, o que esse pedaço do Script faz, é verificar se a variável está vazia. Se estiver vazia, o inSANE dá uma mensagem de erro, notifica que o Scanner não foi encontrado e encerra.
Porém, se o Scanner for detectado, o inSANE envia uma notificação de que o Scanner foi encontrado e prossegue. Com isso, mais uma variável é carregada: O nome do Arquivo que foi escaneado.
Arquivo="$Pasta/Scan_$(date +"%Y-%m-%d_%H-%M-%S").jpg"
Aqui decidi por um formado que tem o prefixo Scan_ e a data e hora em que o escaneamento está ocorrendo. Optei por esse formato, pois eu estava acostumado com eles e o XnViewMP usava o nome do Arquivo nesse formato. E eu gostava, pois não tinha que pensar muito na hora de nomear o arquivo que foi escaneado.
E bom. Já temos o Scanner, o Nome do Arquivo e a Resolução. É hora de juntar tudo num único comando…
scanimage --device "$Scanner" --format=jpeg --output-file "$Arquivo" --resolution "$Resolucao"
… e finalmente escanear a imagem. Para finalizar, o inSANE verifica se tudo ocorreu bem durante o escaneamento, exibe a notificação de que o Escaneamento terminou e encerra o Script.
if [ $? -eq 0 ]; then
echo "Imagem escaneada com sucesso em" "$Pasta" "em" "$Resolucao" "DPI"
notificacoes escaneamentoConcluido &
exit
fi
Conclusão
Bom, esse é um Script que começou bem simples, mas que se expandiu, e mesmo que ele tenha continuado simples, ele ficou bem completo.
E esse Script demonstra uma coisa que adoro no Linux: O poder do Bash e como ele permite que eu faça as minhas próprias soluções. Que é basicamente o que eu criei nos meus últimos Scripts que postei por aqui.
O inSANE acabou sendo o meu primeiro Script mais complexo e é um Script que uso muito até hoje. Do que surgiu de uma preguiça de ter que reinstalar o sistema, pois os programas que eu usava acabaram dando problema, se tornou uma solução que uso até hoje.

