Aqui está um fragmento de um livro futuro, Ferramentas e práticas básicas para um desenvolvedor de software iniciante, de Balthazar Ruberol e Etienne Broad . O livro deve ajudar a educar a geração mais jovem de desenvolvedores. Ele cobrirá temas como dominar o console, configurar e trabalhar de forma eficiente no shell de comando, controle de versão de código usando git
noções básicas de SQL, ferramentas como Make
, jq
e expressões regulares, noções básicas de rede, bem como as melhores práticas para o desenvolvimento de software e colaboração. Os autores estão atualmente trabalhando neste projeto e estão convidando todos a participar da lista de discussão .Conteúdo
Processamento de texto do shell
Um dos motivos que tornam o shell de comando uma ferramenta inestimável é o grande número de comandos de processamento de texto e a capacidade de combiná-los facilmente no pipeline, criando modelos de processamento complexos. Esses comandos tornam muitas tarefas triviais para analisar texto e dados, converter dados entre diferentes formatos, filtrar seqüências de caracteres, etc.Ao trabalhar com dados de texto, o princípio principal é dividir qualquer problema complexo em muitos menores - e resolver cada deles usando uma ferramenta especializada.Faça com que cada programa faça uma coisa bem - The Fundamentals of Unix Philosophy
Os exemplos deste capítulo podem parecer um pouco exagerados à primeira vista, mas isso é feito de propósito. Cada uma das ferramentas foi projetada para resolver um pequeno problema. No entanto, quando combinados, eles se tornam extremamente poderosos.Examinaremos alguns dos comandos de processamento de texto mais comuns e úteis no shell de comando e demonstraremos os fluxos de trabalho reais que os conectam. Sugiro olhar para a mana dessas equipes para ver toda a amplitude de possibilidades à sua disposição.Um arquivo CSV de exemplo está disponível online . Você pode baixá-lo para verificar o material.
gato
O comando é cat
usado para compilar uma lista de um ou mais arquivos e exibir seu conteúdo na tela.$ cat Documents/readme
Thanks again for reading this book!
I hope you're following so far!
$ cat Documents/computers
Computers are not intelligent
They're just fast at making dumb things.
$ cat Documents/readme Documents/computers
Thanks again for reading this book!
I hope you are following so far!
Computers are not intelligent
They're just fast at making dumb things.
cabeça
head
imprime as primeiras n linhas no arquivo. Isso pode ser muito útil para procurar um arquivo de estrutura e formato desconhecido, sem encher o console inteiro com um monte de texto.$ head -n 2 metadata.csv
metric_name,metric_type,interval,unit_name,per_unit_name,description,orientation,integration,short_name
mysql.galera.wsrep_cluster_size,gauge,,node,,The current number of nodes in the Galera cluster.,0,mysql,galera cluster size
Se -n
não especificado, head
imprime as dez primeiras linhas do arquivo ou fluxo de entrada especificado.rabo
tail
- um analógico head
, apenas exibe as últimas n linhas do arquivo.$ tail -n 1 metadata.csv
mysql.performance.queries,gauge,,query,second,The rate of queries.,0,mysql,queries
Se você quiser imprimir todas as linhas localizadas após a linha n (inclusive), você pode usar o argumento -n +n
.$ tail -n +42 metadata.csv
mysql.replication.slaves_connected,gauge,,,,Number of slaves connected to a replication master.,0,mysql,slaves connected
mysql.performance.queries,gauge,,query,second,The rate of queries.,0,mysql,queries
Existem 43 linhas em nosso arquivo, portanto, ele tail -n +42
apenas gera as 42ª e 43ª linhas.Se o parâmetro -n
não for especificado, ele tail
produzirá as últimas dez linhas no arquivo especificado ou no fluxo de entrada.tail -f
ou tail --follow
exiba as últimas linhas do arquivo e cada nova linha conforme elas são gravadas no arquivo. Isso é muito útil para visualizar atividades em tempo real, por exemplo, o que é registrado nos logs do servidor da web etc.banheiro
wc
(contagem de palavras) exibe o número de caracteres ( -c
), palavras ( -w
) ou linhas ( -l
) no arquivo ou fluxo especificado.$ wc -l metadata.csv
43 metadata.csv
$ wc -w metadata.csv
405 metadata.csv
$ wc -c metadata.csv
5094 metadata.csv
Por padrão, todas as opções acima são exibidas.$ wc metadata.csv
43 405 5094 metadata.csv
Se os dados de texto forem canalizados ou redirecionados stdin
, apenas o contador será exibido.$ cat metadata.csv | wc
43 405 5094
$ cat metadata.csv | wc -l
43
$ wc -w < metadata.csv
405
grep
grep
- Trata-se de um filtro de faca suíço, de acordo com um determinado padrão.Por exemplo, podemos encontrar todas as ocorrências da palavra mutex em um arquivo.$ grep mutex metadata.csv
mysql.innodb.mutex_os_waits,gauge,,event,second,The rate of mutex OS waits.,0,mysql,mutex os waits
mysql.innodb.mutex_spin_rounds,gauge,,event,second,The rate of mutex spin rounds.,0,mysql,mutex spin rounds
mysql.innodb.mutex_spin_waits,gauge,,event,second,The rate of mutex spin waits.,0,mysql,mutex spin waits
grep
pode processar arquivos especificados como argumentos ou um fluxo de texto passado para ele stdin
. Assim, podemos concatenar vários comandos grep
para filtrar ainda mais o texto. No exemplo a seguir, filtramos as linhas em nosso arquivo metadata.csv
para encontrar linhas que contenham mutex e OS .$ grep mutex metadata.csv | grep OS
mysql.innodb.mutex_os_waits,gauge,,event,second,The rate of mutex OS waits.,0,mysql,mutex os waits
Vamos considerar algumas opções grep
e seu comportamento.grep -v
Executa a correspondência inversa: filtra as strings que não correspondem ao padrão de argumento.$ grep -v gauge metadata.csv
metric_name,metric_type,interval,unit_name,per_unit_name,description,orientation,integration,short_name
grep -i
Executa correspondência sem distinção entre maiúsculas e minúsculas. O exemplo a seguir grep -i os
localiza OS e OS .$ grep -i os metadata.csv
mysql.innodb.mutex_os_waits,gauge,,event,second,The rate of mutex OS waits.,0,mysql,mutex os waits
mysql.innodb.os_log_fsyncs,gauge,,write,second,The rate of fsync writes to the log file.,0,mysql,log fsyncs
grep -l
Lista arquivos que contêm uma correspondência.$ grep -l mysql metadata.csv
metadata.csv
A equipe grep -c
conta quantas vezes uma amostra foi encontrada.$ grep -c select metadata.csv
3
grep -r
procura recursivamente por arquivos no diretório de trabalho atual e todos os seus subdiretórios.$ grep -r are ~/Documents
/home/br/Documents/computers:Computers are not intelligent
/home/br/Documents/readme:I hope you are following so far!
grep -w
mostra apenas palavras inteiras correspondentes.$ grep follow ~/Documents/readme
I hope you are following so far!
$ grep -w follow ~/Documents/readme
$
cortar
cut
extrai parte do arquivo (ou, como sempre, o fluxo de entrada). O comando define o separador de campos (que separa as colunas) usando a opção -d
e os números das colunas a serem recuperados usando a opção -f
.Por exemplo, o comando a seguir recupera a primeira coluna das últimas cinco linhas do nosso arquivo CSV.$ tail -n 5 metadata.csv | cut -d , -f 1
mysql.performance.user_time
mysql.replication.seconds_behind_master
mysql.replication.slave_running
mysql.replication.slaves_connected
mysql.performance.queries
Como estamos lidando com CSV, as colunas são separadas por vírgula e a opção é responsável por recuperar a primeira coluna -f 1
.Você pode selecionar ambas as primeira e segunda colunas usando a opção -f 1,2
.$ tail -n 5 metadata.csv | cut -d , -f 1,2
mysql.performance.user_time,gauge
mysql.replication.seconds_behind_master,gauge
mysql.replication.slave_running,gauge
mysql.replication.slaves_connected,gauge
mysql.performance.queries,gauge
colar
paste
mescla dois arquivos diferentes em um arquivo de várias colunas.$ cat ingredients
eggs
milk
butter
tomatoes
$ cat prices
1$
1.99$
1.50$
2$/kg
$ paste ingredients prices
eggs 1$
milk 1.99$
butter 1.50$
tomatoes 2$/kg
Por padrão, ele paste
usa um delimitador de guia, mas ele pode ser alterado usando o parâmetro -d
.$ paste ingredients prices -d:
eggs:1$
milk:1.99$
butter:1.50$
tomatoes:2$/kg
Outro caso de uso comum paste
é combinar todas as linhas em um fluxo ou arquivo usando o delimitador especificado, usando uma combinação de -s
e -d
.$ paste -s -d, ingredients
eggs,milk,butter,tomatoes
Se um parâmetro for especificado como um arquivo de entrada -
, ele será lido stdin
.$ cat ingredients | paste -s -d, -
eggs,milk,butter,tomatoes
ordenar
O comando sort
realmente classifica os dados (no arquivo especificado ou no fluxo de entrada).$ cat ingredients
eggs
milk
butter
tomatoes
salt
$ sort ingredients
butter
eggs
milk
salt
tomatoes
sort -r
executa a classificação reversa.$ sort -r ingredients
tomatoes
salt
milk
eggs
butter
sort -n
Classifica os campos pelo seu valor aritmético.$ cat numbers
0
2
1
10
3
$ sort numbers
0
1
10
2
3
$ sort -n numbers
0
1
2
3
10
uniq
uniq
Detecta e filtra linhas idênticas adjacentes no arquivo ou fluxo de entrada especificado.$ cat duplicates
and one
and one
and two
and one
and two
and one, two, three
$ uniq duplicates
and one
and two
and one
and two
and one, two, three
Como uniq
filtra apenas as linhas adjacentes , duplicatas ainda podem permanecer em nossos dados. Para filtrar todas as mesmas linhas de um arquivo, você deve primeiro classificar seu conteúdo.$ sort duplicates | uniq
and one
and one, two, three
and two
uniq -c
no início de cada linha insere o número de suas ocorrências.$ sort duplicates | uniq -c
3 and one
1 and one, two, three
2 and two
uniq -u
Exibe apenas cadeias únicas.$ sort duplicates | uniq -u
and one, two, three
Nota. uniq
É especialmente útil em combinação com a classificação, pois o pipeline | sort | uniq
permite excluir todas as linhas duplicadas em um arquivo ou fluxo.
awk
awk
- Isso é um pouco mais do que apenas uma ferramenta de processamento de texto: na verdade, ele tem uma linguagem de programação inteira . O que é awk
realmente bom é dividir arquivos em colunas e o faz com um brilho especial quando espaços e guias são misturados nos arquivos.$ cat -t multi-columns
John Smith Doctor^ITardis
Sarah-James Smith^I Companion^ILondon
Rose Tyler Companion^ILondon
Nota. cat -t
exibe guias como ^I
.
Como você pode ver, as colunas são separadas por espaços ou tabulações e nem sempre pelo mesmo número de espaços. cut
aqui é inútil porque funciona com apenas um caractere separador. Mas awk
é fácil lidar com esse arquivo.awk '{ print $n }'
exibe a enésima coluna no texto.$ cat multi-columns | awk '{ print $1 }'
John
Sarah-James
Rose
$ cat multi-columns | awk '{ print $3 }'
Doctor
Companion
Companion
$ cat multi-columns | awk '{ print $1,$2 }'
John Smith
Sarah-James Smith
Rose Tyler
Embora seja awk
capaz de muito mais, a saída dos alto-falantes é provavelmente 99% dos casos de uso no meu caso pessoal.Nota. { print $NF }
exibe a última coluna em uma linha.
tr
tr
significa traduzir . Este comando substitui um caractere por outro. Funciona com caracteres ou classes de caracteres como minúsculas, digitadas, espaços alfanuméricos etc.Na entrada padrão, tr <char1> <char2>
substitui todas as ocorrências de <char1> por <char2>.$ echo "Computers are fast" | tr a A
computers Are fAst
tr
pode traduzir classes de caracteres usando notação [:class:]
. Uma lista completa de classes disponíveis é descrita na página de manual tr
, mas algumas são demonstradas aqui.[:space:]
representa todos os tipos de espaços, de espaços simples a guias ou novas linhas.$ echo "computers are fast" | tr '[:space:]' ','
computers,are,fast,%
Todos os caracteres, como espaços, são separados por vírgula. Observe que o caractere %
no final da saída indica a ausência de uma nova linha final. Na verdade, esse personagem também é convertido em vírgula.[:lower:]
representa todos os caracteres em minúsculas e [:upper:]
todos os caracteres em maiúsculas. Assim, a transformação entre eles se torna trivial.$ echo "computers are fast" | tr '[:lower:]' '[:upper:]'
COMPUTERS ARE FAST
$ echo "COMPUTERS ARE FAST" | tr '[:upper:]' '[:lower:]'
computers are fast
tr -c SET1 SET2
converte qualquer caractere não incluído no SET1 em caracteres no SET2. No exemplo a seguir, todos os caracteres, exceto as vogais indicadas, são substituídos por espaços.$ echo "computers are fast" | tr -c '[aeiouy]' ' '
o u e a e a
tr -d
Exclui os caracteres especificados, mas não os substitui. Isto é o equivalente tr <char> ''
.$ echo "Computers Are Fast" | tr -d '[:lower:]'
C A F
tr
também pode substituir os intervalos de caracteres, por exemplo, todas as letras entre a e e ou todos os números entre 1 e 8, usando a notação s-e
, onde s
é o caractere inicial e e
o final.$ echo "computers are fast" | tr 'a-e' 'x'
xomputxrs xrx fxst
$ echo "5uch l337 5p34k" | tr '1-4' 'x'
5uch lxx7 5pxxk
O comando tr -s string1
compacta todas as múltiplas ocorrências de caracteres string1
em um único. Um dos usos mais úteis tr -s
é substituir vários espaços consecutivos por um.$ echo "Computers are fast" | tr -s ' '
Computers are fast
dobra
O comando fold
recolhe todas as linhas de entrada para a largura especificada. Por exemplo, pode ser útil garantir que o texto caiba em telas pequenas. Então, fold -w n
empilha strings de largura n caracteres.$ cat ~/Documents/readme | fold -w 16
Thanks again for
reading this bo
ok!
I hope you're fo
llowing so far!
O comando fold -s
quebrará linhas apenas em caracteres de espaço. Pode ser combinado com o anterior para limitar a sequência ao número especificado de caracteres.Thanks again
for reading
this book!
I hope you're
following so
far!
sed
sed
É um editor de fluxo não interativo usado para converter texto no fluxo de entrada linha por linha. Como entrada, um arquivo ou, ou stdin
, na saída, um arquivo ou stdout
.Os comandos do editor podem incluir um ou mais endereços , uma função e parâmetros . Assim, os comandos são os seguintes:[address[,address]]function[arguments]
Embora ele sed
execute muitas funções, consideraremos apenas substituir o texto como um dos casos de uso mais comuns.Substituição de texto
O comando de substituição é sed
o seguinte:s/PATTERN/REPLACEMENT/[options]
Exemplo : substituindo a primeira instância de uma palavra em cada linha de um arquivo:$ cat hello
hello hello
hello world!
hi
$ cat hello | sed 's/hello/Hey I just met you/'
Hey I just met you hello
Hey I just met you world
hi
Vemos que na primeira linha apenas a primeira instância é substituída hello
. Para substituir todas as ocorrências hello
em todas as linhas, você pode usar a opção g
(significa global ).$ cat hello | sed 's/hello/Hey I just met you/g'
Hey I just met you Hey I just met you
Hey I just met you world
hi
sed
permite usar qualquer delimitador /
, exceto , o que melhora especialmente a legibilidade se houver barras nos próprios argumentos do comando.$ cat hello | sed 's@hello@Hey I just met you@g'
Hey I just met you Hey I just met you
Hey I just met you world
hi
O endereço informa ao editor em qual linha ou intervalo de linhas executar a substituição.$ cat hello | sed '1s/hello/Hey I just met you/g'
Hey I just met you hello
hello world
hi
$ cat hello | sed '2s/hello/Hey I just met you/g'
hello hello
Hey I just met you world
hi
O endereço 1
indica substituir hello
no Hey I just met you
na primeira linha. Podemos especificar o intervalo de endereços na notação <start>,<end>
, onde <end>
pode ser o número da linha ou $
, ou seja, a última linha do arquivo.$ cat hello | sed '1,2s/hello/Hey I just met you/g'
Hey I just met you Hey I just met you
Hey I just met you world
hi
$ cat hello | sed '2,3s/hello/Hey I just met you/g'
hello hello
Hey I just met you world
hi
$ cat hello | sed '2,$s/hello/Hey I just met you/g'
hello hello
Hey I just met you world
hi
Por padrão, ele sed
produz o resultado em sua própria stdout
, mas também pode editar o arquivo original com a opção -i
.$ sed -i '' 's/hello/Bonjour/' sed-data
$ cat sed-data
Bonjour hello
Bonjour world
hi
Nota. No Linux, apenas o suficiente -i
. Mas no macOS, o comportamento do comando é um pouco diferente, então -i
você precisa adicionar logo em seguida ''
.
Exemplos reais
Filtragem CSV com grep e awk
$ grep -w gauge metadata.csv | awk -F, '{ if ($4 == "query") { print $1, "per", $5 } }'
mysql.performance.com_delete per second
mysql.performance.com_delete_multi per second
mysql.performance.com_insert per second
mysql.performance.com_insert_select per second
mysql.performance.com_replace_select per second
mysql.performance.com_select per second
mysql.performance.com_update per second
mysql.performance.com_update_multi per second
mysql.performance.questions per second
mysql.performance.slow_queries per second
mysql.performance.queries per second
Neste exemplo grep
, o arquivo metadata.csv
filtra primeiro as linhas que contêm a palavra gauge
, depois as da query
quarta coluna e exibe o nome da métrica (1ª coluna) com o valor correspondente per_unit_name
(5ª coluna).Exibe o endereço IPv4 associado à interface de rede
$ ifconfig en0 | grep inet | grep -v inet6 | awk '{ print $2 }'
192.168.0.38
O comando ifconfig <interface name>
exibe informações na interface de rede especificada. Por exemplo:en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
ether 19:64:92:de:20:ba
inet6 fe80::8a3:a1cb:56ae:7c7c%en0 prefixlen 64 secured scopeid 0x7
inet 192.168.0.38 netmask 0xffffff00 broadcast 192.168.0.255
nd6 options=201<PERFORMNUD,DAD>
media: autoselect
status: active
Então corra grep
para inet
, isso produzirá duas linhas de correspondência.$ ifconfig en0 | grep inet
inet6 fe80::8a3:a1cb:56ae:7c7c%en0 prefixlen 64 secured scopeid 0x7
inet 192.168.0.38 netmask 0xffffff00 broadcast 192.168.0.255
Em seguida, usando grep -v
excluir a linha com ipv6
.$ ifconfig en0 | grep inet | grep -v inet6
inet 192.168.0.38 netmask 0xffffff00 broadcast 192.168.0.255
Finalmente, com a ajuda, awk
solicitamos a segunda coluna nesta linha: este é o endereço IPv4 associado à nossa interface de rede en0
.$ ifconfig en0 | grep inet | grep -v inet6 | awk '{ print $2 }'
192.168.0.38
Nota. Me ofereceram a substituição por grep inet | grep -v inet6
uma equipe tão confiável awk
:
$ ifconfig en0 | awk ' $1 == "inet" { print $2 }'
192.168.0.38
É mais curto e é direcionado especificamente para IPv4 com a condição $1 == "inet"
.
Recuperando um valor de um arquivo de configuração
$ grep 'editor =' ~/.gitconfig | cut -d = -f2 | sed 's/ //g'
/usr/bin/vim
No arquivo de configuração git do usuário atual, procure o valor editor =
, corte o caractere =
, extraia a segunda coluna e exclua todos os espaços ao redor.$ grep 'editor =' ~/.gitconfig
editor = /usr/bin/vim
$ grep 'editor =' ~/.gitconfig | cut -d'=' -f2
/usr/bin/vim
$ grep 'editor =' ~/.gitconfig | cut -d'=' -f2 | sed 's/ //'
/usr/bin/vim
Extrair IPs de um arquivo de log
O código real a seguir procura uma mensagem no log do banco de dados Too many connections from
(seguido por um endereço IP) e exibe os dez principais invasores.$ grep 'Too many connections from' db.log | \
awk '{ print $12 }' | \
sed 's@/@@' | \
sort | \
uniq -c | \
sort -rn | \
head -n 10 | \
awk '{ print $2 }'
10.11.112.108
10.11.111.70
10.11.97.57
10.11.109.72
10.11.116.156
10.11.100.221
10.11.96.242
10.11.81.68
10.11.99.112
10.11.107.120
Vamos ver o que esse pipeline faz. Primeiro, como é a linha no log.$ grep "Too many connections from" db.log | head -n 1
2020-01-01 08:02:37,617 [myid:1] - WARN [NIOServerCxn.Factory:1.2.3.4/1.2.3.4:2181:NIOServerCnxnFactory@193] - Too many connections from /10.11.112.108 - max is 60
Em seguida, awk '{ print $12 }'
extrai o endereço IP da string.$ grep "Too many connections from" db.log | awk '{ print $12 }'
/10.11.112.108
...
O comando sed 's@/@@'
exclui a barra inicial.$ grep "Too many connections from" db.log | awk '{ print $12 }' | sed 's@/@@'
10.11.112.108
...
Nota. Como vimos anteriormente, sed
você pode usar qualquer separador no. Embora seja geralmente usado como separador /
, aqui estamos substituindo esse caractere em particular, o que prejudicará levemente a legibilidade da expressão de substituição.
sed 's/\///'
sort | uniq -c
classifica os endereços IP em ordem lexicográfica e remove duplicatas, adicionando o número de entradas antes de cada endereço IP.$ grep 'Too many connections from' db.log | \
awk '{ print $12 }' | \
sed 's@/@@' | \
sort | \
uniq -c
1379 10.11.100.221
1213 10.11.103.168
1138 10.11.105.177
946 10.11.106.213
1211 10.11.106.4
1326 10.11.107.120
...
sort -rn | head -n 10
classifica as linhas pelo número de ocorrências, numericamente e na ordem inversa, para que os principais violadores sejam exibidos primeiro, dos quais 10 linhas são exibidas. O último comando awk { print $2 }
recupera os próprios endereços IP.$ grep 'Too many connections from' db.log | \
awk '{ print $12 }' | \
sed 's@/@@' | \
sort | \
uniq -c | \
sort -rn | \
head -n 10 | \
awk '{ print $2 }'
10.11.112.108
10.11.111.70
10.11.97.57
10.11.109.72
10.11.116.156
10.11.100.221
10.11.96.242
10.11.81.68
10.11.99.112
10.11.107.120
Renomeando uma função no arquivo de origem
Imagine que estamos trabalhando em um projeto e gostaríamos de renomear a função com o sobrenome (ou classe, variável etc.) no arquivo de origem. Você pode fazer isso com um comando sed -i
que substitui diretamente no arquivo original.$ cat izk/utils.py
def bool_from_str(s):
if s.isdigit():
return int(s) == 1
return s.lower() in ['yes', 'true', 'y']
$ sed -i 's/def bool_from_str/def is_affirmative/' izk/utils.py
$ cat izk/utils.py
def is_affirmative(s):
if s.isdigit():
return int(s) == 1
return s.lower() in ['yes', 'true', 'y']
Nota. No macOS, sed -i
use em seu lugar sed -i ''
.
No entanto, renomeamos a função apenas no arquivo original. Isso interromperá a importação bool_from_str
em qualquer outro arquivo, pois essa função não está mais definida. Precisamos encontrar uma maneira de renomear em bool_from_str
todo o projeto. Isso pode ser conseguido usando comandos grep
, sed
bem como loops for
ou usando xargs
.Aprofundamento: ciclos for
exargs
Para substituir todas as ocorrências em nosso projeto bool_from_str
, você deve primeiro encontrá-las recursivamente com grep -r
.$ grep -r bool_from_str .
./tests/test_utils.py:from izk.utils import bool_from_str
./tests/test_utils.py:def test_bool_from_str(s, expected):
./tests/test_utils.py: assert bool_from_str(s) == expected
./izk/utils.py:def bool_from_str(s):
./izk/prompt.py:from .utils import bool_from_str
./izk/prompt.py: default = bool_from_str(os.environ[envvar])
Como estamos interessados apenas em arquivos com correspondências, você também deve usar a opção -l/--files-with-matches
:-l, --files-with-matches
Only the names of files containing selected lines are written to standard out-
put. grep will only search a file until a match has been found, making
searches potentially less expensive. Pathnames are listed once per file
searched. If the standard input is searched, the string ``(standard input)''
is written.
$ grep -r --files-with-matches bool_from_str .
./tests/test_utils.py
./izk/utils.py
./izk/prompt.py
Em seguida, podemos usar o comando xargs
para executar ações de cada linha da saída (ou seja, todos os arquivos que contêm a linha bool_from_str
).$ grep -r --files-with-matches bool_from_str . | \
xargs -n 1 sed -i 's/bool_from_str/is_affirmative/'
A opção -n 1
indica que cada linha na saída deve executar um comando separado sed
.Em seguida, os seguintes comandos são executados:$ sed -i 's/bool_from_str/is_affirmative/' ./tests/test_utils.py
$ sed -i 's/bool_from_str/is_affirmative/' ./izk/utils.py
$ sed -i 's/bool_from_str/is_affirmative/' ./izk/prompt.py
Se o comando com o qual você chama xargs
(no nosso caso sed
) suporta vários argumentos, você deve descartá-lo -n 1
para desempenho.grep -r --files-with-matches bool_from_str . | xargs sed -i 's/bool_from_str/is_affirmative/'
Este comando será executado$ sed -i 's/bool_from_str/is_affirmative/' ./tests/test_utils.py ./izk/utils.py ./izk/prompt.py
Nota. A partir da sinopse sed
na página do manual, pode-se ver que a equipe pode aceitar vários argumentos.
SYNOPSIS
sed [-Ealn] command [file ...]
sed [-Ealn] [-e command] [-f command_file] [-i extension] [file ...]
De fato, como vimos no capítulo anterior, isso file ...
significa que vários argumentos são aceitos, que são nomes de arquivos.
Vemos que são feitas substituições para todas as ocorrências bool_from_str
.$ grep -r is_affirmative .
./tests/test_utils.py:from izk.utils import is_affirmative
./tests/test_utils.py:def test_is_affirmative(s, expected):
./tests/test_utils.py: assert is_affirmative(s) == expected
./izk/utils.py:def is_affirmative(s):
./izk/prompt.py:from .utils import is_affirmative
./izk/prompt.py: default = is_affirmative(os.environ[envvar])
Como costuma acontecer, existem várias maneiras de obter o mesmo resultado. Em vez disso, xargs
poderíamos usar loops for
para percorrer as linhas da lista e executar ações em cada item. Esses loops têm a seguinte sintaxe:for item in list; do
command $item
done
Se nós envolvemos o nosso comando grep
em $()
, em seguida, o shell irá executá-lo em um subnível , cujo resultado será então repetido em um loop for
.$ for file in $(grep -r --files-with-matches bool_from_str .); do
sed -i 's/bool_from_str/is_affirmative/' $file
done
Este comando será executado$ sed -i 's/bool_from_str/is_affirmative/' ./tests/test_utils.py
$ sed -i 's/bool_from_str/is_affirmative/' ./izk/utils.py
$ sed -i 's/bool_from_str/is_affirmative/' ./izk/prompt.py
A sintaxe do loop for
me parece mais clara do que isso xargs
, mas o último pode executar comandos em paralelo usando parâmetros -P n
, onde n
é o número máximo de comandos paralelos executados ao mesmo tempo, o que pode proporcionar um ganho de desempenho.Sumário
Todas essas ferramentas abrem um mundo inteiro de possibilidades, pois permitem extrair e transformar dados, criando pipelines inteiros de equipes que talvez nunca foram projetadas para trabalhar juntas. Cada um deles desempenha uma função relativamente pequena (classificação sort
, combinação cat
, filtros grep
, edição sed
, corte cut
, etc.).Qualquer tarefa que inclua texto pode ser reduzida a um pipeline de tarefas menores, cada uma executando uma ação simples e transferindo sua saída para a próxima tarefa.Por exemplo, se quisermos saber quantos endereços IP exclusivos estão no arquivo de log e para que esses endereços IP sempre apareçam na mesma coluna, podemos executar a seguinte sequência de comandos:grep
strings que correspondem ao padrão de strings com endereços IP
- encontre a coluna com o endereço IP, extraia-a com
awk
- classifique a lista de endereços IP usando
sort
- eliminar duplicatas adjacentes com
uniq
- conte o número de linhas (ou seja, endereços IP exclusivos) usando
wc -l
Como existem muitas ferramentas de processamento de texto nativas e de terceiros, também existem várias maneiras de resolver qualquer problema.Os exemplos deste artigo foram exagerados, mas sugiro que você leia o incrível artigo “As ferramentas de linha de comando podem ser 235 vezes mais rápidas que o cluster do Hadoop” para ter uma idéia de quão úteis e poderosos esses comandos realmente são e quais são os problemas reais eles podem decidir.Qual é o próximo
- Conte o número de arquivos e diretórios localizados no seu diretório pessoal.
- .
- , .
- . .
« » (Essential Tools and Practices for the Aspiring Software Developer) , . , , , , git
, SQL, Make
, jq
, , .
, !