sed "s/\(.*\)/\t\1/" $filename > $sedTmpFile && mv $sedTmpFile $filename
Estou esperando que este sed
script insira um tab
na frente de cada linha, $filename
mas não é. Por algum motivo, ele está inserindo um t
.
sed "s/\(.*\)/\t\1/" $filename > $sedTmpFile && mv $sedTmpFile $filename
Estou esperando que este sed
script insira um tab
na frente de cada linha, $filename
mas não é. Por algum motivo, ele está inserindo um t
.
Respostas:
Nem todas as versões de sed
entendem \t
. Basta inserir uma guia literal (pressione Ctrl- e Vdepois Tab).
\t
na parte de substituição da expressão (ele reconheceu \t
na parte de correspondência de padrão muito bem)
Usando o Bash, você pode inserir um caractere TAB programaticamente, assim:
TAB=$'\t'
echo 'line' | sed "s/.*/${TAB}&/g"
echo 'line' | sed 's/.*/'"${TAB}"'&/g' # use of Bash string concatenation
$'string'
explicação, mas falta. Na verdade, eu suspeito que, devido ao uso extremamente estranho, você provavelmente tem um entendimento incompleto (como a maioria de nós faz com o bash). Veja minha explicação abaixo: stackoverflow.com/a/43190120/117471
$TAB
aspas simples, portanto, você precisará usá-lo com aspas duplas.
*
aspas duplas ... isso será tratado como um glob, não como a regex que você pretende.
@sedit estava no caminho certo, mas é um pouco estranho definir uma variável.
A maneira de fazer isso no bash é colocar um cifrão antes da string entre aspas simples.
$ echo -e '1\n2\n3'
1
2
3
$ echo -e '1\n2\n3' | sed 's/.*/\t&/g'
t1
t2
t3
$ echo -e '1\n2\n3' | sed $'s/.*/\t&/g'
1
2
3
Se sua string precisa incluir expansão de variável, você pode colocar strings entre aspas assim:
$ timestamp=$(date +%s)
$ echo -e '1\n2\n3' | sed "s/.*/$timestamp"$'\t&/g'
1491237958 1
1491237958 2
1491237958 3
Em bash $'string'
causa "expansão ANSI-C". E é isso que a maioria de nós esperar quando usamos coisas como \t
, \r
, \n
, etc. De: https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html#ANSI_002dC-Quoting
Palavras da forma $ 'string' são tratadas de maneira especial. A palavra se expande para string , com caracteres de escape de barra invertida substituídos conforme especificado pelo padrão ANSI C. Seqüências de escape de barra invertida, se presentes, são decodificadas ...
O resultado expandido é entre aspas simples, como se o cifrão não estivesse presente.
Eu pessoalmente acho que a maioria dos esforços para evitar o bash são tolos porque evitar o bash NÃO * torna seu código portátil. (Seu código será menos frágil se você explicá-lo do bash -eu
que se você tentar evitar bash e usar sh
[a menos que você seja um ninja POSIX absoluto].) Mas, em vez de ter um argumento religioso sobre isso, vou apenas dar-lhe o MELHOR * responda.
$ echo -e '1\n2\n3' | sed "s/.*/$(printf '\t')&/g"
1
2
3
* Melhor resposta? Sim, porque um exemplo do que a maioria dos scripts de shell anti-bash faria de errado em seu código é usar echo '\t'
como na resposta de @robrecord . Isso funcionará com o GNU echo, mas não com o BSD echo. Isso é explicado pelo The Open Group em http://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html#tag_20_37_16 E este é um exemplo de por que tentar evitar bashismos geralmente falham.
Usei algo assim com um shell Bash no Ubuntu 12.04 (LTS):
Para anexar uma nova linha com tab, segundo quando a primeira é correspondida:
sed -i '/first/a \\t second' filename
Para substituir a primeira pela guia, a segunda :
sed -i 's/first/\\t second/g' filename
\\t
e não \t
.
Use $(echo '\t')
. Você precisará de aspas em torno do padrão.
Por exemplo. Para remover uma guia:
sed "s/$(echo '\t')//"
echo '\t'
vai produzir 2 caracteres separados. A maneira portátil POSIX é usar printf '\t'
. É por isso que digo: não tente tornar seu código portátil não usando o bash. É mais difícil do que você pensa. Usar bash
é a coisa mais portátil que a maioria de nós pode fazer.
Você não precisa usar sed
para fazer uma substituição quando, na verdade, deseja apenas inserir uma guia na frente da linha. A substituição desta caixa é uma operação cara em comparação com apenas imprimi-la, especialmente quando você está trabalhando com arquivos grandes. É mais fácil de ler também, pois não é regex.
por exemplo, usando awk
awk '{print "\t"$0}' $filename > temp && mv temp $filename
sed
não suporta \t
, nem outras sequências de escape como \n
para esse assunto. A única maneira que descobri de fazer isso foi realmente inserir o caractere de tabulação no script usando sed
.
Dito isso, você pode querer considerar o uso de Perl ou Python. Aqui está um pequeno script Python que escrevi e uso para todas as expressões regulares de fluxo:
#!/usr/bin/env python
import sys
import re
def main(args):
if len(args) < 2:
print >> sys.stderr, 'Usage: <search-pattern> <replace-expr>'
raise SystemExit
p = re.compile(args[0], re.MULTILINE | re.DOTALL)
s = sys.stdin.read()
print p.sub(args[1], s),
if __name__ == '__main__':
main(sys.argv[1:])
Em vez de BSD sed, eu uso perl:
ct@MBA45:~$ python -c "print('\t\t\thi')" |perl -0777pe "s/\t/ /g"
hi
Eu acho que os outros ter esclarecido este adequadamente para outras abordagens ( sed
, AWK
, etc.). No entanto, bash
seguem minhas respostas específicas (testadas no macOS High Sierra e CentOS 6/7).
1) Se o OP quiser usar um método de pesquisa e substituição semelhante ao que propôs originalmente, eu sugeriria o uso perl
para isso, como segue. Notas: barras invertidas antes dos parênteses para regex não devem ser necessárias, e esta linha de código reflete como $1
é melhor usar do que \1
com o perl
operador de substituição (por exemplo, por documentação do Perl 5 ).
perl -pe 's/(.*)/\t$1/' $filename > $sedTmpFile && mv $sedTmpFile $filename
2) No entanto, como apontado por ghostdog74 , uma vez que a operação desejada é simplesmente adicionar uma guia no início de cada linha antes de alterar o arquivo tmp para o arquivo de entrada / destino ( $filename
), recomendo perl
novamente, mas com a seguinte modificação (s):
perl -pe 's/^/\t/' $filename > $sedTmpFile && mv $sedTmpFile $filename
## OR
perl -pe $'s/^/\t/' $filename > $sedTmpFile && mv $sedTmpFile $filename
3) Claro, o arquivo tmp é supérfluo , então é melhor apenas fazer tudo 'no lugar' (adicionando -i
sinalizador) e simplificar as coisas para uma linha mais elegante com
perl -i -pe $'s/^/\t/' $filename