- Dave McKay
@TheGurkha
>br>>>li>Abril 16, 2020, 8:00am EDT
Pode parecer uma loucura, mas o comando Linux sed
é um editor de texto sem interface. Pode utilizá-lo a partir da linha de comando para manipular texto em ficheiros e streams. Vamos mostrar-lhe como aproveitar o seu poder.
O Poder do sed
O comando sed
é um pouco como o xadrez: leva uma hora a aprender o básico e uma vida inteira a dominá-lo (ou, pelo menos, muita prática). Vamos mostrar-lhe uma selecção de gambits de abertura em cada uma das categorias principais de sed
funcionalidade.
sed
é um editor de fluxo que trabalha com entrada ou ficheiros de texto canalizados. No entanto, não tem uma interface interactiva de editor de texto. Em vez disso, fornece instruções para que ele siga à medida que funciona através do texto. Tudo isto funciona em Bash e outros shells de linha de comando.
Com sed
pode fazer tudo o que se segue:
- Seleccionar texto
- Substituir texto
- Adicionar linhas ao texto
- Eliminar linhas do texto
- Modificar (ou preservar) um ficheiro original
Temos estruturado os nossos exemplos para introduzir e demonstrar conceitos, não produzir os comandos mais curtos (e menos acessíveis) sed
comandos. Contudo, as funcionalidades de correspondência de padrões e selecção de texto de sed
dependem fortemente de expressões regulares (regexes). Vai precisar de alguma familiaridade com estas para obter o melhor de sed
.
RELATADO: Como usar expressões regulares (regexes) no Linux
Um exemplo simples
P>Primeiro, vamos usar echo
para enviar algum texto para sed
através de um tubo, e ter sed
substituir uma parte do texto. Para tal, digitamos o seguinte:
echo howtogonk | sed 's/gonk/geek/'
O comando echo
envia “howtogonk” para sed
, e a nossa regra de substituição simples (o “s” significa substituição) é aplicada. sed
procura no texto de entrada a ocorrência da primeira string, e substituirá qualquer correspondência pela segunda.
A string “gonk” é substituída por “geek”, e a nova string é impressa na janela do terminal.
Substituições são provavelmente o uso mais comum de sed
. Antes de podermos mergulhar mais fundo nas substituições, porém, precisamos de saber como seleccionar e combinar texto.
Selecting Text
Vamos precisar de um ficheiro de texto para os nossos exemplos. Vamos utilizar um que contenha uma selecção de versos do poema épico de Samuel Taylor Coleridge “The Rime of the Ancient Mariner”.”
Digitamos o seguinte para dar uma vista de olhos com less
:
less coleridge.txt
Para seleccionar algumas linhas do ficheiro, fornecemos as linhas de início e fim do intervalo que queremos seleccionar. Um único número selecciona que uma linha.
Para extrair linhas uma a quatro, digitamos este comando:
sed -n '1,4p' coleridge.txt
Nota a vírgula entre 1
e 4
. O p
significa “imprimir linhas combinadas”. Por defeito, sed
imprime todas as linhas. Veríamos todo o texto no ficheiro com as linhas combinadas impressas duas vezes. Para evitar isto, usaremos a opção -n
(silenciosa) para suprimir o texto sem correspondência.
Alteramos os números das linhas para podermos seleccionar um verso diferente, como mostrado abaixo:
sed -n '6,9p' coleridge.txt
Podemos usar a opção -e
(expressão) para fazer múltiplas selecções. Com duas expressões, podemos seleccionar dois versos, como assim:
sed -n -e '1,4p' -e '31,34p' coleridge.txt
Se reduzirmos o primeiro número na segunda expressão, podemos inserir um espaço em branco entre os dois versos. Escrevemos o seguinte:
sed -n -e '1,4p' -e '30,34p' coleridge.txt
Também podemos escolher uma linha inicial e dizer sed
para percorrer o ficheiro e imprimir linhas alternativas, a cada quinta linha, ou para saltar qualquer número de linhas. O comando é semelhante às que utilizámos acima para seleccionar um intervalo. Desta vez, contudo, usaremos um til (~
) em vez de uma vírgula para separar os números.
O primeiro número indica a linha de partida. O segundo número indica sed
quais as linhas após a linha inicial que queremos ver. O número 2 significa cada segunda linha, 3 significa cada terceira linha, e assim por diante.
Escrevemos o seguinte:
sed -n '1~2p' coleridge.txt
Nem sempre saberá onde se encontra o texto que procura no ficheiro, o que significa que os números de linha nem sempre serão de grande ajuda. Contudo, também pode usar sed
para seleccionar linhas que contenham padrões de texto correspondentes. Por exemplo, vamos extrair todas as linhas que começam com “And.”
O caret (^
) representa o início da linha. Anexaremos o nosso termo de pesquisa em barras de corte para a frente (/
). Também incluímos um espaço após “E” para que palavras como “Android” não sejam incluídas no resultado.
Leitura sed
scripts podem ser um pouco difíceis no início. O /p
significa “imprimir”, tal como fez nos comandos que utilizámos acima. No seguinte comando, no entanto, uma barra de avanço precede-o:
Três linhas que começam com “And” são extraídas do ficheiro e exibidas para nós.
Fazer Substituições
No nosso primeiro exemplo, mostramos-lhe o seguinte formato básico para um sed
substituição:
echo howtogonk | sed 's/gonk/geek/'
O s
diz sed
isto é uma substituição. A primeira cadeia é o padrão de pesquisa, e a segunda é o texto com o qual queremos substituir esse texto correspondente. Claro que, como em todas as coisas Linux, o diabo está nos detalhes.
Digitamos o seguinte para alterar todas as ocorrências de “dia” para “semana”, e damos ao marinheiro e ao albatroz mais tempo para se ligar:
sed -n 's/day/week/p' coleridge.txt
Na primeira linha, apenas a segunda ocorrência de “dia” é alterada. Isto é porque sed
pára após a primeira partida por linha. Temos de adicionar um “g” no final da expressão, como mostrado abaixo, para realizar uma pesquisa global de modo a que todos os resultados em cada linha sejam processados:
sed -n 's/day/week/gp' coleridge.txt
Isto corresponde a três dos quatro da primeira linha. Como a primeira palavra é “Day,” e sed
é sensível a maiúsculas e minúsculas, não considera essa instância como “day”.”
Digitamos o seguinte, adicionando um i
ao comando no fim da expressão para indicar a insensibilidade a maiúsculas e minúsculas:
sed -n 's/day/week/gip' coleridge.txt
Isto funciona, mas pode não querer ligar sempre a insensibilidade a maiúsculas e minúsculas para tudo. Nesses casos, pode utilizar um grupo regex para adicionar insensibilidade a casos específicos de padrões.
Por exemplo, se incluirmos caracteres entre parênteses rectos (), eles são interpretados como “qualquer caracter desta lista de caracteres”.”
Digitamos o seguinte, e incluímos “D” e “d” no grupo, para garantir que corresponde tanto a “Day” como a “day”:
sed -n 's/ay/week/gp' coleridge.txt
Também podemos restringir as substituições a secções do ficheiro. Digamos que o nosso ficheiro contém um espaçamento estranho no primeiro verso. Podemos usar o seguinte comando familiar para ver o primeiro verso:
sed -n '1,4p' coleridge.txt
Procuraremos por dois espaços e substituímo-los por um. Faremos isto globalmente para que a acção se repita ao longo de toda a linha. Para ser claro, o padrão de pesquisa é espaço, asterisco de espaço (*
), e a cadeia de substituição é um único espaço. O 1,4
restringe a substituição às primeiras quatro linhas do ficheiro.
Juntamos tudo isso no seguinte comando:
sed -n '1,4 s/ */ /gp' coleridge.txt
Isto funciona muito bem! O padrão de pesquisa é o que é importante aqui. O asterisco (*
) representa zero ou mais do carácter anterior, que é um espaço. Assim, o padrão de pesquisa está à procura de cordas de um espaço ou mais.
Se substituirmos um único espaço por qualquer sequência de espaços múltiplos, devolveremos o ficheiro ao espaçamento regular, com um único espaço entre cada palavra. Isto também substituirá um único espaço por um único espaço em alguns casos, mas isto não afectará nada adversário – ainda assim obteremos o nosso resultado desejado.
Se digitarmos o seguinte e reduzirmos o padrão de pesquisa a um único espaço, verá imediatamente porque temos de incluir dois espaços:
sed -n '1,4 s/ */ /gp' coleridge.txt
P>Porque o asterisco corresponde a zero ou mais do carácter anterior, vê cada carácter que não é um espaço como um “espaço zero” e aplica a substituição ao mesmo.
No entanto, se incluirmos dois espaços no padrão de pesquisa, sed
deve encontrar pelo menos um caractere de espaço antes de aplicar a substituição. Isto assegura que os caracteres não espaciais permanecerão intocados.
Digitamos o seguinte, utilizando o -e
(expressão) que utilizámos anteriormente, o que nos permite fazer duas ou mais substituições em simultâneo:
sed -n -e 's/motion/flutter/gip' -e 's/ocean/gutter/gip' coleridge.txt
Podemos obter o mesmo resultado se usarmos um ponto e vírgula (;
) para separar as duas expressões, desta forma:
sed -n 's/motion/flutter/gip;s/ocean/gutter/gip' coleridge.txt
Quando trocamos “day” por “week” no comando seguinte, o exemplo de “day” na expressão “well a-day” foi também trocado:
sed -n 's/ay/week/gp' coleridge.txt
Para evitar isto, só podemos tentar substituições em linhas que correspondam a outro padrão. Se modificarmos o comando para ter um padrão de pesquisa no início, só consideraremos operar em linhas que correspondam a esse padrão.
Digitamos o seguinte para fazer o nosso padrão de correspondência a palavra “depois”:
sed -n '/after/ s/ay/week/gp' coleridge.txt
Que nos dá a resposta que queremos.
Mais Substituições Complexas
Demos uma pausa ao Coleridge e utilizemos sed
para extrair nomes do ficheiro etc/passwd
.
Há formas mais curtas de o fazer (mais sobre isso mais tarde), mas utilizaremos a forma mais longa aqui para demonstrar outro conceito. Cada item combinado num padrão de pesquisa (chamado subexpressões) pode ser numerado (até um máximo de nove itens). Pode então usar estes números no seu sed
comandos para referenciar subexpressões específicas.
Tem de incluir a subexpressão entre parênteses para que isto funcione. Os parênteses também devem ser precedidos por uma barra invertida (\
) para evitar que sejam tratados como um carácter normal.
Para o fazer, escreveria o seguinte:
sed 's/\(*\).*/\1/' /etc/passwd
Vamos decompor isto:
-
sed 's/
: O comandosed
e o início da expressão de substituição. -
\(
: O parêntese de abertura que encerra a subexpressão, precedido por uma barra invertida (\
). -
*
: A primeira subexpressão do termo de pesquisa contém um grupo entre parênteses rectos. O caret (^
) significa “não” quando usado num grupo. Um grupo significa qualquer carácter que não seja um dois pontos (:
) será aceite como um parenteco. -
\)
: O parêntese de fecho com uma barra invertida anterior (\
). -
.*
: Esta segunda subexpressão de pesquisa significa “qualquer carácter e qualquer número deles”.” -
/\1
: A parte de substituição da expressão contém1
precedida de uma contrabarra (\
). Isto representa o texto que corresponde à primeira subexpressão. -
/'
: A barra invertida de fecho (/
) e a única citação ('
) termina o comandosed
.
O que tudo isto significa é que vamos procurar qualquer cadeia de caracteres que não contenha dois pontos (:
), que será a primeira instância de correspondência de texto. Depois, vamos procurar qualquer outra coisa nessa linha, que será a segunda instância de correspondência de texto. Vamos substituir a linha inteira pelo texto que corresponde à primeira subexpressão.
Cada linha no /etc/passwd
o ficheiro começa com um nome de utilizador com dois pontos. Fazemos corresponder tudo até aos primeiros dois pontos, e depois substituímos esse valor por toda a linha. Assim, isolámos os nomes de utilizador.
P>Próximo, vamos incluir a segunda subexpressão entre parênteses para podermos referi-la também por número. Substituiremos também \1
por \2
. O nosso comando irá agora substituir toda a linha por tudo, desde os primeiros dois pontos (:
) até ao fim da linha.
Escrevemos o seguinte:
sed 's/\(*\)\(.*\)/\2/' /etc/passwd
As pequenas alterações invertem o significado do comando, e obtemos tudo excepto os nomes de utilizador.
Agora, vamos ver a forma rápida e fácil de o fazer.
O nosso termo de pesquisa é desde os primeiros dois pontos (:
) até ao fim da linha. Como a nossa expressão de substituição está vazia (//
), não substituiremos o texto correspondente por nada.
Então, escrevemos o seguinte, cortando tudo desde os primeiros dois pontos (:
) até ao fim da linha, deixando apenas os nomes de utilizador:
sed 's/:.*//" /etc/passwd
>p>Vejamos um exemplo em que referimos a primeira e a segunda correspondência no mesmo comando.
Temos um ficheiro de vírgulas (,
) separando o primeiro e o último nome. Queremos listá-los como “sobrenome, nome próprio”. Podemos usar cat
, como mostrado abaixo, para ver o que está no ficheiro:
cat geeks.txt
Como muitos comandos sed
, este próximo pode parecer impenetrável no início:
sed 's/^\(.*\),\(.*\)$/\2,\1 /g' geeks.txt
Este é um comando de substituição como os outros que utilizámos, e o padrão de pesquisa é bastante fácil. Vamos quebrá-lo abaixo:
-
sed 's/
: O comando de substituição normal. -
^
: Porque o carpete não está num grupo (), significa “O início da linha”
-
\(.*\),
: A primeira subexpressão é qualquer número de quaisquer caracteres. Está entre parênteses, cada um dos quais é precedido por uma barra invertida (\
) para que possamos referi-la por número. Todo o nosso padrão de pesquisa até agora traduz-se como pesquisa desde o início da linha até à primeira vírgula (,
) para qualquer número de quaisquer caracteres. -
\(.*\)
: A próxima subexpressão é (novamente) qualquer número de quaisquer caracteres. Está também entre parênteses, ambos precedidos por uma barra invertida (\
) para que possamos referenciar o texto correspondente por número. -
$/
: O sinal do dólar ($
) representa o fim da linha e permitirá que a nossa pesquisa continue até ao fim da linha. Utilizámo-lo simplesmente para introduzir o sinal do dólar. Não precisamos realmente dele aqui, pois o asterisco (*
) iria para o fim da linha neste cenário. A barra oblíqua frontal (/
) completa a secção do padrão de pesquisa. - : Como incluímos as nossas duas subexpressões entre parênteses, podemos referir-nos a ambas pelos seus números. Porque queremos inverter a ordem, escrevemo-las como
second-match,first-match
. Os números têm de ser precedidos por uma barra invertida (\
). -
/g
: Isto permite que o nosso comando funcione globalmente em cada linha. -
geeks.txt
: O ficheiro em que estamos a trabalhar.
Também pode usar o comando Cut (c
) para substituir linhas inteiras que correspondam ao seu padrão de pesquisa. Escrevemos o seguinte para procurar uma linha com a palavra “pescoço” nela, e substituímos por uma nova cadeia de texto:
sed '/neck/c Around my wrist was strung' coleridge.txt
A nossa nova linha aparece agora no fundo do nosso extracto.
Inserção de Linhas e Texto
Podemos também inserir novas linhas e texto no nosso ficheiro. Para inserir novas linhas depois de quaisquer linhas correspondentes, usaremos o comando Append (a
).
Aqui está o ficheiro com que vamos trabalhar:
cat geeks.txt
Nós numerámos as linhas para tornar isto um pouco mais fácil de seguir.
Digitamos o seguinte para procurar linhas que contenham a palavra “He”, e inserimos uma nova linha por baixo delas:
sed '/He/a --> Inserted!' geeks.txt
Digitamos o seguinte e incluímos o comando Insert (i
) para inserir a nova linha acima daquelas que contêm texto correspondente:
sed '/He/i --> Inserted!' geeks.txt
Podemos usar o comando ampersand (&
), que representa o texto original correspondente, para adicionar novo texto a uma linha correspondente. \1
\2
, e assim por diante, representam subexpressões correspondentes.
Para adicionar texto ao início de uma linha, usaremos um comando de substituição que corresponde a tudo na linha, combinado com uma cláusula de substituição que combina o nosso novo texto com a linha original.
Para fazer tudo isto, digitamos o seguinte:
sed 's/.*/--> Inserted &/' geeks.txt
Digitamos o seguinte, incluindo o comando G
, que adicionará uma linha em branco entre cada linha:
sed 'G' geeks.txt
Se quiser adicionar duas ou mais linhas em branco, pode usar G;G
G;G;G
, e assim por diante.
Deleting Lines
O comando Delete (d
) elimina as linhas que correspondem a um padrão de pesquisa, ou aquelas especificadas com números de linha ou intervalos.
Por exemplo, para apagar a terceira linha, escreveríamos o seguinte:
sed '3d' geeks.txt
Para apagar o intervalo de linhas quatro a cinco, escreveríamos o seguinte:
sed '4,5d' geeks.txt
Para apagar linhas fora de um intervalo, usaríamos um ponto de exclamação (!
), como mostrado abaixo:
sed '6,7!d' geeks.txt
Saving Your Changes
Até agora, todos os nossos resultados foram impressos na janela terminal, mas ainda não os salvámos em lado nenhum. Para tornar estas alterações permanentes, pode ou escrever as suas alterações no ficheiro original ou redireccioná-las para um novo ficheiro.
Overbitar o seu ficheiro original requer alguma precaução. Se o seu comando sed
estiver errado, pode fazer algumas alterações ao ficheiro original que são difíceis de desfazer.
Para alguma paz de espírito, sed
pode criar uma cópia de segurança do ficheiro original antes de executar o seu comando.
Pode usar a opção In-place (-i
) para dizer sed
para escrever as alterações ao ficheiro original, mas se lhe adicionar uma extensão de ficheiro, sed
fará uma cópia de segurança do ficheiro original para um novo ficheiro. Terá o mesmo nome que o ficheiro original, mas com uma nova extensão de ficheiro.
Para demonstrar, procuraremos quaisquer linhas que contenham a palavra “Ele” e apagá-las-emos. Também faremos uma cópia de segurança do nosso ficheiro original para um novo, utilizando a extensão BAK.
Para fazer tudo isto, digitamos o seguinte:
sed -i'.bak' '/^.*He.*$/d' geeks.txt
Digitamos o seguinte para nos certificarmos de que o nosso ficheiro de cópia de segurança permanece inalterado:
cat geeks.txt.bak
Também podemos digitar o seguinte para redireccionar a saída para um novo ficheiro e obter um resultado semelhante:
sed -i'.bak' '/^.*He.*$/d' geeks.txt > new_geeks.txt
Usamos cat
para confirmar que as alterações foram escritas no novo ficheiro, como mostrado abaixo:
cat new_geeks.txt
Having sed All That
Como já deve ter notado, mesmo este primer rápido em sed
é bastante longo. Há muito a fazer com este comando, e há ainda mais que se pode fazer com ele.
Esperançosamente, no entanto, estes conceitos básicos forneceram uma base sólida sobre a qual se pode construir à medida que se continua a aprender mais.
Dave McKay usou pela primeira vez computadores quando a fita de papel perfurado estava em voga, e tem vindo a programar desde então. Após mais de 30 anos na indústria das TI, é agora um jornalista de tecnologia a tempo inteiro. Durante a sua carreira, trabalhou como programador freelance, gestor de uma equipa internacional de desenvolvimento de software, gestor de projectos de serviços de TI e, mais recentemente, como responsável pela protecção de dados. Dave é um evangelista Linux e defensor do código aberto. Leia a biografia completa ”