Os maiores programas em Shell do mundo
(github.com/oils-for-unix)- Esta wiki não é apenas uma lista de scripts longos, mas reúne programas Shell “substanciais”, escritos manualmente e que usam até estruturas de dados e algoritmos
- O critério é, em geral, mais de 5 mil linhas, e scripts gerados automaticamente ou scripts de completion repetitivos ficam fora dos casos principais
- Entre os exemplos de destaque estão o ble.sh com 87 mil linhas, kalua com cerca de 56 mil SLoC/linhas, Relax-and-Recover com 35 mil linhas, nb com 26 mil linhas e winetricks com 22 mil linhas, indo muito além da percepção comum sobre scripts Shell
- A lista inclui amplamente ferramentas de uso real, como editor interativo de linha, addon para OpenWRT, depurador de Bash, ferramenta de teste de TLS, implementação de Kubernetes, ferramenta de backup e recuperação, ferramenta de emissão de certificados e monitor de recursos
- O teste OSH “Wild” faz parsing de mais de um milhão de linhas de Shell, mas a maior parte disso são programas pequenos e definições repetitivas de pacotes de distribuições, então é tratado separadamente de grandes programas Shell
O que conta como um “grande programa Shell”
- Aqui, “biggest” não significa apenas número bruto de linhas, mas algo “substantial”, ou seja, com escala e complexidade reais
- Em princípio, entram apenas scripts Shell escritos manualmente
- Scripts grandes gerados pelo autoconf são tratados como exceção
- Artefatos gerados automaticamente, como o script de 70 mil linhas do coreutils, não são considerados grandes programas Shell no sentido substantivo
- Há atenção especial para programas Shell que usam estruturas de dados e algoritmos
- O bash-completion é sofisticado, mas por ter uma estrutura repetitiva com funções relativamente simples para cada comando de uma máquina Unix, fica mais como contraexemplo
- A linha de corte aproximada é mais de 5 mil linhas
- Os maiores programas Shell não repetitivos normalmente ficam na faixa de 10 mil+ linhas
- Ainda não foi confirmado nenhum programa acima de 100 mil linhas
Exemplos dos maiores programas Shell
- akinomyoga/ble.sh: total de 87 mil linhas, 63 mil LoC sem comentários
- É um editor interativo de linha parecido com o fish, escrito em bash puro
- O arquivo principal
out/ble.shtem 39 mil linhas, 29 mil LoC sem comentários, e com os módulos o total passa de 80 mil linhas - Há muitos comentários em japonês
- Usa
bind -xpara ler os bytes brutos do terminal, faz a decodificação manualmente com várias máquinas de estado explícitas e mantém/atualiza um drawing buffer - Também inclui timing e “fibers”
- Detalhes sobre o parser Shell estão nos comentários da issue 663, e o projeto é considerado um dos exemplos mais sofisticados de uso de estruturas de dados em Shell
- Há tentativas de executá-lo no OSH, e a maior parte já é parseada
- O primeiro commit tinha 8 mil linhas / 6 mil LoC em 2015, e o desenvolvimento real começou em 2013
- kalua: addon para OpenWRT, escrito em POSIX shell com cerca de 56 mil SLoC/linhas
- Relax-and-Recover: ferramenta de backup e recuperação com 35 mil linhas e 24 mil LoC
- O primeiro commit no git foi em março de 2009, quando tinha 4 mil linhas / 3 mil LoC
- xwmx/nb: o próprio
nbtem 26 mil linhas e 22 mil LoC em bash- Se os testes em bats forem contados como bash, há mais 91 mil linhas e 61 mil LoC adicionais
- O primeiro commit é de 2014, e o histórico ativo de commits começa no início de 2016
- vegardit/bash-funk: biblioteca Bash com 27 mil linhas no total e 24 mil LoC
- O primeiro commit foi em maio de 2017, quando tinha 10 mil linhas / 8 mil LoC
- winetricks: script Shell de 22 mil linhas que instala vários programas Windows sobre o Wine
- drwetter/testssl.sh: arquivo único com 21 mil linhas de bash
- Parece ter sido escrito manualmente
- Começou em 2006 com alguns comandos
openssl - Durante o parsing, trava na issue #606
- rkhunter: programa em Bourne shell com 21 mil linhas, escrito de 2003 a 2018
- O site oficial é rkhunter.sourceforge.net
- Simplenetes: apresentado como “Kubernetes in 17K lines of Shell”
- É marcado como um caso surpreendente, mas parece estar em estado dormente
- Há um thread relacionado no Hacker News
- inxi 2.3.56: programa em bash com 16 mil linhas, marcado como obsoleto
- Foi derivado de
infobashem 2008 - Na época,
infobashtinha 889 linhas, e havia começado em 2005 - A partir da v2.9, o
inxifoi substituído por uma implementação em Perl
- Foi derivado de
- bashdb: depurador de Bash com cerca de 14 mil linhas em bash
- Como contexto relacionado, há Implementing Debuggers
- romkatv/powerlevel10k: há 12 mil linhas de script zsh no diretório
internal/- Além disso, configs e helper scripts somam mais 8 mil linhas
- O primeiro commit é de 2014
- dylanaraps/neofetch: programa de 10 mil linhas escrito para Bash 3.2 que mostra informações do sistema
- Também consegue fazer algumas coisas interessantes relacionadas a imagens
- O primeiro commit é de 2015
- distrobox: script bash com mais de 7 mil linhas que permite usar qualquer distribuição Linux dentro do terminal
- acme.sh: script Shell de 8 mil linhas para emitir e renovar certificados
Implementações menores, mas marcantes
- bashforth: com cerca de 3.800 linhas, não é tão grande, mas implementa uma linguagem de programação de verdade
- Há muitos espaços em branco e comentários
- yoda: tem cerca de metade do tamanho do bashforth, mas implementa o interpretador e o compilador completos
- É uma implementação do mesmo autor, 20 anos mais nova, com mais funcionalidades
- Vem com o comentário “What learned you have, unlearn you must!”
Outros programas Shell relevantes
- abcde / A Better CD Encoder: usado para ripar CDs e tem cerca de 5,5 mil LoC
- thc-segfault: 3,3 mil LoC, um servidor pubnix feito majoritariamente em Bash
- ffmpeg/configure: script
configuredo FFmpeg, escrito manualmente, com 8,4 mil LoC - ffhevc: wrapper totalmente escrito à mão em Bash para codificar vídeo HEVC com FFmpeg e libx265, com 4 mil LoC
- ffx264: wrapper totalmente escrito à mão em Bash para codificar vídeo H.264/AVC com FFmpeg e libx264, com 3,9 mil LoC
- h264enc: wrapper totalmente escrito à mão em Bash para codificar vídeo H.264/AVC com MEncoder, com 9,2 mil LoC
- bashtop: monitor de recursos com 5,3 mil LoC
- halcyon: sistema de instalação de apps Haskell com 6,6 mil LoC
- Código escrito manualmente, com atenção à semântica do bash e à checagem de erros, num estilo único inspirado em programação funcional
- wordshell: cerca de 7 mil linhas de código para gerenciar vários sites WordPress pela linha de comando
- BaCon: cerca de 10 mil linhas, converte programas escritos em BASIC para C
- Há tanto uma implementação em BASIC quanto uma implementação em Shell script
- FireHOL: o script principal tem 9 mil linhas, e a ferramenta FireQOS soma mais 3 mil linhas
- É uma linguagem e também um programa executável para criar um firewall seguro e stateful a partir de uma configuração legível por humanos
- gxadmin: 11 mil LoC, um conjunto de consultas SQL templated e utilitários de processamento de dados para administrar o motor de workflows científicos Galaxy
- mulle-bashfunctions: biblioteca de funções para bash/zsh com cerca de 6 mil linhas
- É usada no
mulle-sde, que por sua vez é outro Shell script com 100 mil linhas
- É usada no
- x11docker: 11,6 mil linhas, executa aplicações GUI em contêineres docker ou podman
Linguagens semelhantes a Shell e DSLs
- modernish: dialeto shell portátil escrito em Shell
- bats: DSL para escrever testes que gera código bash
- bashible: DSL semelhante ao Ansible, escrita em bash
- clash: framework orientado a objetos compatível com todos os shells POSIX modernos
- bash Infinity: biblioteca padrão e framework boilerplate para bash
Programas menores e ecossistema relacionado
- Scripts de Alpine, Aboriginal e Debian aparecem em um post de blog separado
- Scripts de completion são grandes, mas muitas vezes repetitivos
- O completion de Zsh para _git tem 8,3 mil linhas de código
git-completion.bashe o completion do Docker também são citados como exemplos
- dyne/Tomb: script zsh com cerca de 3.500 linhas
- Basalt: gerenciador de pacotes full-featured escrito em Bash puro
- Tem alguns milhares de linhas, mas conta com um ecossistema rico, com mais de 15 apps e bibliotecas
- bash-core: biblioteca que estende os builtins
trapeshopte adiciona stacktrace e recursos essenciais de conveniência - bash-object: biblioteca em Bash puro para construir estruturas de dados arbitrariamente aninhadas, com quase 200 testes
- bash-json: biblioteca em Bash puro para fazer parsing e saída de JSON
- tablespoon/fun/cli-clock: relógio textual multilinha escrito em bash
- json.bash / jb: ferramenta de linha de comando e biblioteca bash para gerar JSON
- Tem cerca de 1.700 linhas, e os testes somam cerca de 3 mil linhas
Testes do OSH e cuidados ao usar Shell
- OSH "Wild" Tests fazem parsing de mais de um milhão de linhas de Shell
- A maior parte disso são programas pequenos e definições repetitivas de pacotes de distribuições, como Alpine
PKGBUILDe Gentooebuild
- A maior parte disso são programas pequenos e definições repetitivas de pacotes de distribuições, como Alpine
- Shell Programs That Run Under OSH aponta para uma lista de programas Shell executados no OSH
- shell script are dangerous alerta que Shell é uma linguagem para lidar com o interior do sistema em modo interativo ou não interativo, cheia de recursos, muito perigosa e não criada para desenvolver aplicações
1 comentários
Comentários do Hacker News
Ao investigar, descobri que o OMS era um enorme conjunto de scripts shell rodando em servidores AIX, que tinha evoluído por mais de 10 anos e depois sido abandonado. O código passava de 50 mil linhas; pedidos, pagamentos e outras informações eram movidos entre servidores via FTP e depois analisados com
sed/awkcomplexos; o estoque também era rastreado em arquivos de texto e transferido por FTPNa época, Perl parecia a opção mais prática para migrar aquela bagunça, então comecei pelas partes mais simples, substituindo-as por pequenos módulos Perl e refatorando tudo gradualmente dentro de uma aplicação Perl maior. Em 3 meses, reduzi tudo para cerca de 5 mil linhas de Perl, e as falhas do sistema original praticamente desapareceram, com desempenho 10 a 100 vezes maior. Foi horrível, mas continua sendo um dos trabalhos mais satisfatórios que já fiz
Fico curioso se você leu todo o código original e o entendeu profundamente antes de reproduzir o comportamento com precisão, ou se descartou grandes blocos e reescreveu conforme achava que “deveria funcionar”. Também me pergunto se havia muito código boilerplate que podia ser substituído rapidamente
O primeiro script realmente grande que escrevi foi um instalador de cerca de 7 mil linhas para Enrust CA e diretório, e ele precisava rodar em praticamente todos os Unix da época. Não começou assim, mas cresceu conforme as demandas dos clientes
A instalação em si não era tão complicada, mas os upgrades eram um pouco complexos, e naquela época cada Unix tinha todos os utilitários ligeiramente diferentes. Uma boa parte do script era código para detectar e gerenciar essas diferenças, além de detecção de erros, recuperação, rollback e um gerenciamento muito primitivo de pacotes e dependências
O Unix da DEC — o que não era Ultrix — era o mais obscuro. Levei dias para perceber que todos os utilitários de linha de comando cortavam a saída na largura das colunas do terminal, e ainda me lembro disso 30 anos depois
O HP-UX tinha mudanças incompatíveis a cada release e, se minha memória não falha, demos suporte da versão 6.5 até a 11. Quase não me lembro de Ultrix, do lado da Novell, NeXT e Sequent. Lembro que AIX era estranho, mas esqueci o motivo. Os três/quatro sistemas operacionais da Sun também tinham diferenças, mas os manuais eram excelentes e eram os melhores
wcno binário principal e nas bibliotecas auxiliares de um projeto e, até agora, são 6.224 linhasEra um script para gerenciar pipelines com garantia linear, compostos por adaptadores de protocolo de entrada, um ou mais filtros e adaptadores de protocolo de saída. O objetivo era permitir que pessoas que não fossem especialistas em contêineres ou protocolos, mas soubessem como queriam que os arquivos fossem filtrados e transformados ao passar pelo pipeline, pudessem usá-lo
O binário de nível superior tem uma estrutura com subfunções, como
git [ git options ] < git action> [action options]ousystemctl. Há até um subcomando que adiciona novos subcomandos, cria as bibliotecas necessárias e preenche previamente definições de funções a partir de templates. Os templates têm funções de uso curto/longo, de modo quecbap -houcbap pipeline -hforneçam orientações úteisHá subcomandos para manipular imagens base, componentes e pipelines. Boa parte do código é para testes que verificam se as definições de componentes e pipelines foram escritas corretamente. O formato dos pipelines é quase TOML, então há código para parsing de TOML e para converter seções em arrays; os componentes são arquivos simples
key=value, então há código para extrair lado esquerdo e direito e validar o schemaComo os componentes do pipeline podem compartilhar propriedades, também há código para encontrar propriedades comuns nos arquivos
vareetce definir propriedades dos componentes. Há muitas funções para manipular usuários, grupos, diretórios e FIFOs conforme requisitos de segurança. Ao configurar um pipeline, ele cria e aplica usuários, grupos, tipos SELinux e categorias MCS, e depois os mapeia para arquivos de serviço que iniciam os componentes, então também há muita manipulação de systemdO maior conjunto de chamadas provavelmente é o das funções que obtêm e configuram propriedades de componentes — na prática, propriedades de contêineres. Para cada propriedade, criei uma função de obtenção, uma função de validação e uma versão inline dentro do pipeline, para tornar a definição de contêineres orientada a dados o mais flexível possível
Também há código que usa muitas referências do Bash para definir variáveis a partir de arquivos, variáveis de ambiente e linha de comando, o que permite testes rápidos. Ele oferece suporte a quatro níveis de usuários: mantenedores que lidam com o próprio código, desenvolvedores que criam definições de componentes, integradores que montam pipelines a partir de componentes e operadores que instalam pipelines; e consegue copiar e empacotar a si mesmo para exportação a usuários de cada nível
Como o sistema de destino pode ser qualquer Linux, ele é empacotado e extraído com
makeself. Por exemplo, quando um integrador cria uma definição de pipeline, é gerado um arquivomakeself; ao executá-lo no sistema de destino, ele cria todos os usuários, grupos, diretórios e FIFOs — ou seja, o IPC entre componentes —, aplica DAC/MAC, cria arquivos systemd, copia as imagens para cada usuário e então executa o pipeline. Com a opção de remoção, tudo isso também pode ser revertidoTambém há um pouco de seccomp, mas isso ficou pausado enquanto encontramos o equilíbrio entre listas de permissão e listas de bloqueio. Uso ShellCheck de forma realmente rigorosa
Também não dá para esperar que todo ambiente tenha
bc/dc, e algumas das máquinas que eu tinha usavam versões antigas do Bash, com suporte muito limitado a arrays associativos. Como compromisso, escolhi AWK como alvo; AWK é uma linguagem de uso geral muito mais agradável do que a maioria dos shells e está presente em qualquer ambiente POSIX: https://beyondloom.com/blog/lila.htmlbc/dcem todos os ambientesFiquei bastante surpreso ao perceber que a instalação do Ubuntu no WSL2 aparentemente não vinha com
bc/dc. Para cálculos de ponto flutuante, uso AWK, mas simplesmente chamando-o como um processo externo.Como alguém que escreveu e manteve grandes programas em Perl várias vezes ao longo da carreira, há um motivo para as pessoas fazerem isso
Linguagens como Java ou Python se encaixam bem quando as interfaces e os formatos estão definidos e há pouca interação com o SO. Se você usa JSON/XML/YAML ou se comunica com bancos de dados e outros programas via HTTP(S), cria-se a situação ideal em que essas linguagens brilham
Mas, ao lidar com grandes volumes de texto e interação com o SO, Java e Python viram um grande sofrimento. Por outro lado, Shell/Perl parecem muito mais fáceis para esse tipo de tarefa
Quase todo trabalho de automação, interfaces confusas e não padronizadas, arquivos de texto/logs, formatos de dados não estruturados ou insuficientemente estruturados entram nessa categoria. Some a isso a compatibilidade retroativa do Perl, sua ampla base instalada e seu desempenho, e praticamente não há alternativa ao Perl para esse tipo de trabalho
Há muito tempo venho achando que uma das grandes razões pelas quais grandes empresas hoje empregam milhares de pessoas em trabalho manual para fazer coisas trivialmente automatizáveis é o declínio do uso de Perl. Ao tentar fazer grandes automações em Python ou Java, as pessoas logo desistem, cansadas da verbosidade do código que precisam escrever e manter e da escala total do projeto
Por isso, agora provavelmente é necessário mais pessoal caro em regime integral. Se o usuário final está no Windows, já existe uma opção parecida com Perl no desktop. É o PowerShell, que cumpre um papel semelhante ao do Perl
Bash+grep tende facilmente a cair no padrão de iniciar um novo processo para cada linha de texto. Para fazer isso de forma eficiente, é preciso minimizar o trabalho; para isso, é necessário processamento em lote e deduplicação. Isso significa lidar com os dados de forma com estado, acompanhando o contexto de deduplicação, o que é mais fácil em uma linguagem de programação de verdade
Bash+grep é vantajoso para processamento de texto sem estado, então é fácil acumular muito trabalho duplicado. Outra forma de reduzir trabalho é fazer filtragem precisa, algo que é mais fácil expressar de forma imperativa e limpa em uma linguagem adequada. grep e expressões regulares não servem nem um pouco para esse uso
Ao usar formatos linha a linha, o git adiciona escapes para tentar aceitar qualquer coisa, mas o suporte não é consistente, e é possível desativá-lo solicitando strings terminadas por nulo com a opção
-z. Parece que o Bash não tem como lidar com isso, enquanto linguagens suficientemente de baixo nível tratam isso de forma natural. Também é possível fazer streaming incremental sem precisar iniciar um novo processo para cada linha de textoComo bônus, dá para usar uma única base de código para todas as tarefas, haja HTTP no meio ou qualquer outra coisa
Há alguns links para contexto. “Estamos reinventando o Perl?”: https://www.oilshell.org/blog/2021/01/why-a-new-shell.html#a...
“O shell Unix deveria evoluir como o Perl 5, com uma opção de upgrade compatível, e não como um big bang ao estilo Perl 6/Raku”: https://www.oilshell.org/blog/2020/07/blog-roadmap.html#the-...
Tour pelo YSH: https://www.oilshell.org/release/latest/doc/ysh-tour.html
Isso pode não parecer uma grande vantagem até você trabalhar em um ambiente onde não dá para instalar coisas da internet, ou que nem sequer tem acesso à internet
O problema central ao escrever programas grandes como scripts Bash é que a linguagem de shell script, para começo de conversa, não foi projetada para complexidade
Ela é excelente para coordenar pequenos comandos e conectar rapidamente ferramentas existentes de forma exploratória, mas, quando Bash começa a passar de algumas centenas de linhas, aparecem em sequência limitações que tornam a manutenção de longo prazo e a escalabilidade difíceis
Primeiro, a legibilidade é um problema. A sintaxe do Bash pode ficar realmente obscura conforme cresce. As regras de escopo de variáveis são sutis, o tratamento de erros é primitivo e o tratamento de strings fica bagunçado rapidamente. No fim, mantenedores perdem tempo decifrando o que está acontecendo, e também fica difícil fazer alterações com confiança
Em seguida vem a falta de ferramentas robustas. Linguagens mais maduras têm ferramentas de análise estática, linters e depuradores que ajudam a detectar erros comuns cedo. No Bash, essas coisas não existem ou são muito limitadas. Sem essas proteções, programas grandes em Bash ficam mais vulneráveis a erros silenciosos, regressões e bugs sutis
Testes também são um problema. É possível testar scripts Bash, mas o processo costuma ser mais trabalhoso, e fica ainda mais complicado quando há lógica ou estruturas de dados complexas. Ao lidar com casos de borda, como espaços em nomes de arquivos ou condições de ambiente inesperadas, você acaba com muito código defensivo doloroso de validar
Por fim, o próprio ecossistema não foi feito para desenvolvimento em Bash em grande escala. Perde-se modularização, gerenciamento de pacotes, tratamento padronizado de dependências e os padrões modernos de desenvolvimento que Python ou Go oferecem. Com o tempo, essas ausências se acumulam e reduzem a velocidade
Usar Bash para tarefas pontuais ou automações simples é bom. É isso que o Bash faz bem. Mas, se você pretende construir algo grande, normalmente é melhor usar uma linguagem projetada para criar e manter aplicações complexas; mesmo que a curva de aprendizado inicial ou a configuração sejam um pouco maiores, isso economiza tempo no longo prazo
Usar o ShellCheck como linter ajuda a pegar muitas armadilhas comuns. Bash/shell têm mesmo muitas armadilhas e comportamentos inesperados, a ponto de até autores experientes de Bash poderem cair nelas
Ainda assim, Bash/shell ocupam uma posição peculiar na hierarquia das linguagens. Estão em praticamente todo lugar e é bem provável que continuem existindo daqui a 30 anos. Se você quer um programa que rode em quase qualquer lugar e ainda rode daqui a 30 anos, shell/Bash é uma boa escolha
Nem é um script escrito muito tempo atrás, então não sei por que escolheram Bash. O script funciona, mas dá a sensação de que alguma coisa vai quebrar só de olhar torto para o código
-xpara depuração/rastreamentoO maior programa shell escrito manualmente que eu usava regularmente no passado provavelmente era o abcde (A Better CD Encoder), com cerca de 5.500 linhas
https://abcde.einval.com
https://git.einval.com/cgi-bin/gitweb.cgi?p=abcde.git;a=blob...
Muitos desses programas são verdadeiras joias. Por exemplo, o script rkhunter tem um código razoável, ainda tem espaço para melhorias, e também é um tesouro de informações
Grande parte do tamanho do código desses scripts é usada para garantir, em várias plataformas, que os utilitários necessários existam e que se comportem como esperado com diferentes opções de linha de comando. Esse é o ponto mais doloroso para quem escreve scripts shell sérios, ainda mais difícil do que sinais e subprocessos
Se o rkhunter tivesse sido escrito em uma linguagem de programação “de verdade”, acho que essas informações seriam menos transparentes. Poderiam ter sido empurradas para registros dentro de estruturas de dados e consultadas ali, ou operadas por várias funções sobre estruturas de dados aninhadas — ou, pior, por uma combinação de métodos e classes. Talvez os logs fossem fatiados em JSON, comprimidos em um banco de dados e acessados por outros métodos
Como scripts shell não têm esse ferramental complexo, eles tendem, em vez disso, a expor diretamente o que está acontecendo. Por isso o rkhunter também serve como uma documentação razoável sobre vários exploits e rootkits, com menos necessidade de cavar de arquivo em arquivo, de estrutura em estrutura, de banco de dados em banco de dados
O cliente do FreeBSD Update tem cerca de 3.600 linhas de código sh
Não é grande comparado a outros programas mencionados aqui, mas acho que o volume de funcionalidade de uma “ferramenta que atualiza o sistema operacional inteiro” é bem significativo. O código que constrói as atualizações fica dividido em vários arquivos, então somado deve dar ainda mais
poudrieretem, em código sh, cerca de três vezes o cliente do FreeBSD Update: https://github.com/freebsd/poudriere/blob/master/src/share/p...São “apenas” 7,1 mil linhas, mas um dos meus favoritos é o script acme.sh, usado para emitir e renovar certificados no Let’s Encrypt
https://github.com/acmesh-official/acme.sh/blob/master/acme....
Às vezes, o shell é a única coisa que dá para garantir que estará disponível, e há situações em que portabilidade é indispensável
Mas, em geral, se você tem uma aplicação shell gigantesca, talvez seja hora de repensar suas escolhas de vida
Além disso, normalmente não dá para fazer muita coisa só com o shell; você precisa de comandos como
find,grep,sed,cat,head,tailecut. Esses comandos também têm seus próprios problemas de portabilidadeMirar no BusyBox pode ser a melhor opção, mas, no momento em que você sai de um sistema Linux comum, escrever scripts Bourne shell portáveis fica difícil ou quase impossível
Com um compilador C, você pode sair do shell escrevendo programas em C para o script shell compor, ou instalar uma linguagem de script melhor, como Lua. Hoje em dia, os casos em que é obrigatório usar apenas shell parecem ser bem de nicho