Dezenas de truques com shell Linux, o que poderia economizar seu tempo



  • Primeiro de tudo, você pode ler este artigo em russo aqui .

Uma noite, eu estava lendo Mastering expressões regulares de Jeffrey Friedl , percebi que, mesmo que você tenha toda a documentação e muita experiência, poderia haver muitos truques desenvolvidos por pessoas diferentes e aprisionados por eles mesmos. Todas as pessoas são diferentes. E técnicas que são óbvias para certas pessoas podem não ser óbvias para outras pessoas e parecer algum tipo de mágica estranha para terceira pessoa. A propósito, eu já descrevi vários momentos aqui (em russo) .

Para o administrador ou o usuário, a linha de comando não é apenas uma ferramenta que pode fazer tudo, mas também uma ferramenta altamente personalizada que pode ser desenvolvida para sempre. Recentemente, houve um artigo traduzido sobre alguns truques úteis na CLI. Mas acho que o tradutor não tem experiência suficiente com a CLI e não seguiu os truques descritos; portanto, muitas coisas importantes podem ser perdidas ou mal interpretadas.

Sob o corte - uma dúzia de truques no shell Linux da minha experiência pessoal.

Nota: Todos os scripts e exemplos do artigo foram especialmente simplificados o máximo possível - portanto, talvez você consiga achar vários truques completamente inúteis - talvez seja esse o motivo. Mas, de qualquer forma, compartilhe suas opiniões nos comentários!

1. String dividida com expansões variáveis


As pessoas costumam usar recortar ou até awk apenas para subtrair uma parte da corda por padrão ou com separadores.
Além disso, muitas pessoas usam a operação de substring bash usando $ {VARIABLE: start_position: length}, que funciona muito rápido.

Mas o bash fornece uma maneira poderosa de manipular as strings de texto usando #, ##,% e %% - ele é chamado de expansões de variáveis ​​do bash .
Usando esta sintaxe, você pode cortar o necessário pelo padrão sem executar comandos externos, para que ele funcione muito rápido.

O exemplo abaixo mostra como obter a terceira coluna (shell) da string em que os valores são separados por dois pontos "nome de usuário: homedir: shell" usando cut ou usando expansões variáveis ​​(usamos o *: mask e o comando ##, que significa: cut todos os caracteres à esquerda até os últimos dois pontos encontrados):

$ STRING="username:homedir:shell" $ echo "$STRING"|cut -d ":" -f 3 shell $ echo "${STRING##*:}" shell 

A segunda opção não inicia o processo filho ( corte ) e não usa tubos, o que deve funcionar muito mais rápido. E se você estiver usando o subsistema bash no Windows, onde os tubos mal se movem, a diferença de velocidade será significativa .

Vamos ver um exemplo no Ubuntu - execute nosso comando em um loop por 1000 vezes

 $ cat test.sh #!/usr/bin/env bash STRING="Name:Date:Shell" echo "using cut" time for A in {1..1000} do cut -d ":" -f 3 > /dev/null <<<"$STRING" done echo "using ##" time for A in {1..1000} do echo "${STRING##*:}" > /dev/null done 

Resultados
 $ ./test.sh using cut real 0m0.950s user 0m0.012s sys 0m0.232s using ## real 0m0.011s user 0m0.008s sys 0m0.004s 

A diferença é várias dezenas de vezes!

Obviamente, o exemplo acima é muito artificial. No exemplo real, não trabalharemos com uma string estática, queremos ler um arquivo real. E para o comando ' cut ', apenas redirecionamos / etc / passwd para ele. No caso de ##, precisamos criar um loop e ler o arquivo usando o comando interno ' read '. Então, quem vencerá este caso?

 $ cat test.sh #!/usr/bin/env bash echo "using cut" time for count in {1..1000} do cut -d ":" -f 7 </etc/passwd > /dev/null done echo "using ##" time for count in {1..1000} do while read do echo "${REPLY##*:}" > /dev/null done </etc/passwd done 
Resultado
 $ ./test.sh $ ./test.sh using cut real 0m0.827s user 0m0.004s sys 0m0.208s using ## real 0m0.613s user 0m0.436s sys 0m0.172s 
Sem comentários =)

Mais alguns exemplos:

Extraia o valor após o caractere igual:

 $ VAR="myClassName = helloClass" $ echo ${VAR##*= } helloClass 

Extrair texto entre colchetes:

 $ VAR="Hello my friend (enemy)" $ TEMP="${VAR##*\(}" $ echo "${TEMP%\)}" enemy 

2. Autocompletar bash com guia


O pacote bash-complete faz parte de quase todos os distribuidores Linux. Você pode habilitá-lo em /etc/bash.bashrc ou /etc/profile.d/bash_completion.sh, mas geralmente ele já está ativado por padrão. Em geral, o preenchimento automático é um dos primeiros momentos convenientes no shell do Linux que um novato encontra antes de tudo.

Mas o fato de que nem todos usam todos os recursos de conclusão do bash e, na minha opinião, é completamente inútil. Por exemplo, nem todo mundo sabe que o preenchimento automático funciona não apenas com nomes de arquivos, mas também com aliases, nomes de variáveis, nomes de funções e, para alguns comandos, mesmo com argumentos. Se você se interessar por scripts de preenchimento automático, que na verdade são scripts de shell, pode até adicionar o preenchimento automático para seu próprio aplicativo ou script.
Mas vamos voltar aos pseudônimos.

Você não precisa editar a variável PATH ou criar arquivos no diretório especificado para executar o alias. Você só precisa adicioná-los ao perfil ou script de inicialização e executá-los em qualquer lugar.

Normalmente, estamos usando letras minúsculas para arquivos e diretórios no * nix, portanto, pode ser muito confortável criar aliases em maiúsculas - nesse caso, o bash-complete irá adivinhar seu comando quase com uma única letra:

 $ alias TAsteriskLog="tail -f /var/log/asteriks.log" $ alias TMailLog="tail -f /var/log/mail.log" $ TA[tab]steriksLog $ TM[tab]ailLog 

3. Autocompletar bash com guia - parte 2


Para casos mais complicados, provavelmente você gostaria de colocar seus scripts pessoais em $ HOME / bin.
Mas temos funções no bash.

As funções não requerem caminho ou arquivos separados. E a conclusão do bash (atenção) também funciona com funções.

Vamos criar a função LastLogin em .profile (não esqueça de recarregar .profile):

 function LastLogin { STRING=$(last | head -n 1 | tr -s " " " ") USER=$(echo "$STRING"|cut -d " " -f 1) IP=$(echo "$STRING"|cut -d " " -f 3) SHELL=$( grep "$USER" /etc/passwd | cut -d ":" -f 7) echo "User: $USER, IP: $IP, SHELL=$SHELL" } 

(Na verdade, não é importante o que essa função está fazendo, é apenas um script de exemplo que podemos colocar no script separado ou até no pseudônimo, mas a função poderia ser melhor) .

No console (observe que o nome da função tem uma primeira letra maiúscula para acelerar a conclusão do bash):

 $ L[tab]astLogin User: saboteur, IP: 10.0.2.2, SHELL=/bin/bash 

4.1 Dados sensíveis


Se você colocar espaço antes de qualquer comando no console, ele não aparecerá no histórico de comandos; portanto, se você precisar colocar uma senha de texto sem formatação no comando, é uma boa maneira de usar esse recurso - veja o exemplo abaixo, echo "hello 2 " não aparecerá no histórico:

 $ echo "hello" hello $ history 2 2011 echo "hello" 2012 history 2 $ echo "my password secretmegakey" # there are two spaces before 'echo' my password secretmegakey $ history 2 2011 echo "hello" 2012 history 2 

É opcional
Geralmente é ativado por padrão, mas você pode configurar esse comportamento na seguinte variável:

exportar HISTCONTROL = ignoreboth


4.2 Dados sensíveis nos argumentos da linha de comando


Você deseja armazenar alguns scripts de shell no git para compartilhá-los entre servidores ou pode ser parte do script de inicialização do aplicativo. E você deseja que esse script se conecte ao banco de dados ou faça qualquer outra coisa que exija credenciais.

Obviamente, é uma má idéia armazenar credenciais no próprio script, porque o git não é seguro.

Geralmente você pode usar variáveis, que já foram definidas nos ambientes de destino, e seu script não conterá as senhas em si.

Por exemplo, você pode criar um script pequeno em cada ambiente com 700 permissões e chamá-lo usando o comando source do script principal:

 secret.sh PASSWORD=LOVESEXGOD 

 myapp.sh source ~/secret.sh sqlplus -l user/"$PASSWORD"@database:port/sid @mysqfile.sql 

Mas não é seguro.

Se alguém puder fazer login no seu host, ele poderá executar o comando ps e ver seu processo sqlplus com todos os argumentos da linha de comando, incluindo senhas. Portanto, ferramentas seguras geralmente devem poder ler senhas / chaves / dados confidenciais diretamente dos arquivos.

Por exemplo - secure ssh simplesmente não tem opções para fornecer senha na linha de comando. Mas ele pode ler a chave ssh no arquivo (e você pode definir permissões seguras no arquivo de chave ssh).

E o wget não seguro tem a opção "--password", que permite fornecer a senha na linha de comando. E todo o tempo em que o wget estiver em execução, todos poderão executar o comando ps e ver a senha que você forneceu.

Além disso, se você possui muitos dados confidenciais e deseja controlá-los a partir do git, a única maneira é a criptografia. Assim, você coloca em cada ambiente de destino apenas a senha mestra e todos os outros dados que você pode criptografar e colocar no git. E você pode trabalhar com dados criptografados na linha de comando, usando a interface CLI openssl. Aqui está um exemplo para criptografar e descriptografar da linha de comando:

O arquivo secret.key contém a chave mestra - uma única linha:

 $ echo "secretpassword" > secret.key; chmod 600 secret.key 

Vamos usar aes-256-cbc para criptografar uma string:

 $ echo "string_to_encrypt" | openssl enc -pass file:secret.key -e -aes-256-cbc -a U2FsdGVkX194R0GmFKCL/krYCugS655yLhf8aQyKNcUnBs30AE5lHN5MXPjjSFML 

Você pode colocar essa string criptografada em qualquer arquivo de configuração armazenado no git ou em qualquer outro lugar - sem secret.key, é quase impossível descriptografá-lo.
Para descriptografar, execute o mesmo comando, substitua -e por -d:

 $ echo 'U2FsdGVkX194R0GmFKCL/krYCugS655yLhf8aQyKNcUnBs30AE5lHN5MXPjjSFML' | openssl enc -pass file:secret.key -d -aes-256-cbc -a string_to_encrypt 

5. O comando grep


Todos devem conhecer o comando grep. E seja amigável com expressões regulares. E muitas vezes você pode escrever algo como:

 tail -f application.log | grep -i error 

Ou mesmo assim:

 tail -f application.log | grep -i -P "(error|warning|failure)" 

Mas não se esqueça que o grep tem muitas opções maravilhosas. Por exemplo -v, que reverte sua pesquisa e mostra todas as mensagens, exceto as "informações":

 tail -f application.log | grep -v -i "info" 

Material adicional:

A opção -P é muito útil, porque, por padrão, o grep usa "expressão regular básica" bastante desatualizada: e -P ativa o PCRE, que nem conhece o agrupamento.
-i ignora maiúsculas e minúsculas.
--line-buffered analisa a linha imediatamente em vez de esperar para alcançar o buffer padrão de 4k (útil para tail -f | grep).

Se você conhece bem a expressão regular, com --only-matching / -o, você pode realmente fazer grandes coisas com o corte de texto. Basta comparar os próximos dois comandos para extrair o shell do myuser:

 $ grep myuser /etc/passwd| cut -d ":" -f 7 $ grep -Po "^myuser(:.*){5}:\K.*" /etc/passwd 

O segundo comando parece mais compilado, mas executa apenas grep em vez de grep e cut , portanto, levará menos tempo para execução.

6. Como reduzir o tamanho do arquivo de log


No * nix, se você excluir o arquivo de log atualmente usado por um aplicativo, não poderá apenas remover todos os logs, mas evitar que o aplicativo grave novos logs até reiniciar.

Como o descritor de arquivo não abre o nome do arquivo, mas a estrutura do iNode, o aplicativo continuará gravando no descritor de arquivo no arquivo, que não possui entrada de diretório, e esse arquivo será excluído automaticamente após a parada do aplicativo pelo sistema de arquivos ( o aplicativo pode abra e feche o arquivo de log sempre que desejar gravar algo para evitar esse problema, mas isso afeta o desempenho ).

Então, como limpar o arquivo de log sem excluí-lo:

 echo "" > application.log 

Ou podemos usar o comando truncate:

 truncate --size=1M application.log 

Mencione, que o comando truncado excluirá o restante do arquivo, para que você perca os últimos eventos de log. Veja outro exemplo de como armazenar as últimas 1000 linhas:

 echo "$(tail -n 1000 application.log)" > application.log 

PS No Linux, temos rotatelog de serviço padrão. Você pode adicionar seus logs para truncar / girar automaticamente ou usar bibliotecas de logs existentes que podem fazer isso por você (como log4j em java).

7. Assista está olhando para você!


Existe uma situação em que você está aguardando a conclusão de algum evento. Por exemplo, enquanto outro usuário faz logon no shell (você executa continuamente quem comanda), ou alguém deve copiar o arquivo para sua máquina usando scp ou ftp e você está aguardando a conclusão (repetindo várias dezenas de vezes).

Nesses casos, você pode usar

 watch <command> 

Por padrão, será executado a cada 2 segundos com a pré-limpeza da tela até Ctrl + C pressionado. Você pode configurar com que frequência deve ser executado.

É muito útil quando você deseja assistir a registros ao vivo.

8. Sequência de basquete


Existe uma construção muito útil para criar intervalos. Por exemplo, em vez de algo como isto:

 for srv in 1 2 3 4 5; do echo "server${srv}";done server1 server2 server3 server4 server5 

Você pode escrever o seguinte:

 for srv in server{1..5}; do echo "$srv";done server1 server2 server3 server4 server5 

Além disso, você pode usar o comando seq para gerar intervalos formatados. Por exemplo, podemos usar seq para criar valores que serão ajustados automaticamente pela largura (00, 01 em vez de 0, 1):

 for srv in $(seq -w 5 10); do echo "server${srv}";done server05 server06 server07 server08 server09 server10 

Outro exemplo com substituição de comando - renomeie arquivos. Para obter o nome do arquivo sem extensão, estamos usando o comando ' basename ':

 for file in *.txt; do name=$(basename "$file" .txt);mv $name{.txt,.lst}; done 

Ainda mais curto com '%':

 for file in *.txt; do mv ${file%.txt}{.txt,.lst}; done 

PS Na verdade, para renomear os arquivos, você pode tentar a ferramenta ' renomear ', que possui muitas opções.

Outro exemplo - vamos criar estrutura para o novo projeto java:

 mkdir -p project/src/{main,test}/{java,resources} 

Resultado
 project/ !--- src/ |--- main/ | |-- java/ | !-- resources/ !--- test/ |-- java/ !-- resources/ 

9. cauda, ​​vários arquivos, vários usuários ...


Mencionei o multitail para ler arquivos e assistir a vários logs ao vivo. Mas não é fornecido por padrão, e as permissões para instalar algo nem sempre estão disponíveis.

Mas a cauda padrão também pode:

 tail -f /var/logs/*.log 

Também vamos lembrar sobre os usuários, que estão usando aliases 'tail -f' para assistir aos logs do aplicativo.
Vários usuários podem assistir arquivos de log simultaneamente usando 'tail -f'. Alguns deles não são muito precisos com suas sessões. Eles podem deixar 'tail -f' em segundo plano por algum motivo e esquecê-lo.

Se o aplicativo foi reiniciado, existem esses processos 'tail -f' em execução que estão assistindo ao arquivo de log inexistente podem travar por vários dias ou até meses.

Geralmente não é um grande problema, mas não ordenadamente.

Caso esteja usando o alias para observar o log, você pode modificar esse alias com a opção --pid:

 alias TFapplog='tail -f --pid=$(cat /opt/app/tmp/app.pid) /opt/app/logs/app.log' 

Nesse caso, todas as caudas serão automaticamente encerradas quando o aplicativo de destino for reiniciado.

10. Crie um arquivo com tamanho especificado


O dd foi uma das ferramentas mais populares para trabalhar com dados binários e de bloco. Por exemplo, criar um arquivo de 1 MB preenchido com zero será:

 dd if=/dev/zero of=out.txt bs=1M count=10 

Mas eu recomendo usar fallocate :

 fallocate -l 10M file.txt 

Nos sistemas de arquivos que suportam a função de alocação (xfs, ext4, Btrfs ...), o fallocate será executado instantaneamente, ao contrário da ferramenta dd. Além disso, alocar significa alocação real de blocos, não a criação de um arquivo sobressalente.

11. xargs


Muitas pessoas conhecem o comando xargs popular. Mas nem todos eles usam duas opções a seguir, o que poderia melhorar muito o seu script.

Primeiro - você pode obter uma lista muito longa de argumentos para processar e pode exceder o comprimento da linha de comando (por padrão, ~ 4 kb).

Mas você pode limitar a execução usando a opção -n, para que o xargs execute o comando várias vezes, enviando um número especificado de argumentos por vez:

 $ # lets print 5 arguments and send them to echo with xargs: $ echo 1 2 3 4 5 | xargs echo 1 2 3 4 5 $ # now let's repeat, but limit argument processing by 3 per execution $ echo 1 2 3 4 5 | xargs -n 3 echo 1 2 3 4 5 

Indo em frente. O processamento de uma lista longa pode levar muito tempo, porque é executado em um único encadeamento. Mas se tivermos vários núcleos, podemos dizer ao xargs para rodar em paralelo:

 echo 1 2 3 4 5 6 7 8 9 10| xargs -n 2 -P 3 echo 

No exemplo acima, dizemos ao xargs para processar a lista em 3 threads; cada thread recebe e processa 2 argumentos por execução. Se você não sabe quantos núcleos você possui, vamos otimizar isso usando " nproc ":

 echo 1 2 3 4 5 6 7 8 9 10 | xargs -n 2 -P $(nproc) echo 

12. dorme? enquanto? leia!


Algum tempo você precisa esperar alguns segundos. Ou aguarde a entrada do usuário com read:

 read -p "Press any key to continue " -n 1 

Mas você pode simplesmente adicionar a opção de tempo limite para ler o comando, e seu script será pausado por um período especificado de segundos, mas no caso de execução interativa, o usuário pode facilmente ignorar a espera.

 read -p "Press any key to continue (auto continue in 30 seconds) " -t 30 -n 1 

Então você pode simplesmente esquecer o comando de suspensão.

Suspeito que nem todos os meus truques pareçam interessantes, mas me pareceu que uma dúzia é um bom número a ser preenchido.

Digo adeus e serei grato por participar da pesquisa.

Claro, sinta-se à vontade para discutir o que foi dito acima e compartilhar seus truques legais nos comentários!

Source: https://habr.com/ru/post/pt444890/


All Articles