2010-04-09 1 views
63
sed "s/\(.*\)/\t\1/" $filename > $sedTmpFile && mv $sedTmpFile $filename 

Mi aspetto che questo script sed inserisca una scheda nel carattere di ogni riga in $filename ma non lo è. Per qualche motivo si sta inserendo una t invece .. Strana ..Perché sed non riconosce t come una scheda?

+1

Come sed può variare tra le piattaforme (in particolare, BSD/MacOSX rispetto a Linux), può essere utile specificare la piattaforma su cui si sta utilizzando sed. – Isaac

+0

@Isaac, questo era su Solaris – sixtyfootersdude

+0

sed "s/\ (. * \)/# \ 1 /" $ nomefile | tr '#' '\ t'> $ sedTmpFile && mv $ sedTmpFile $ nomefile. – user2432405

risposta

80

Non tutte le versioni di sed comprendono \t. Basta invece inserire una scheda letterale (premere Ctrl - V quindi Scheda).

+2

Ah si; per chiarire: non tutte le versioni di sed comprendono '\ t' nella parte di sostituzione dell'espressione (ha riconosciuto' \ t' nella parte di corrispondenza del modello giusta) –

+1

awwwwwwwwwwwwwwww, ok che è piuttosto interessante. E strano Perché dovresti farlo riconoscere in un posto ma non nell'altro ...? – sixtyfootersdude

+2

Chiamato da uno script, non funzionerà: le schede verrebbero ignorate da sh. Ad esempio, il seguente codice da uno script di shell aggiungerà $ TEXT_TO_ADD, senza anteporre esso da una tabulazione: sed "$ {LINE} un \\ \t $ TEXT_TO_ADD " $ FILE . – Dereckson

0

sed non supporta \t, né altre sequenze di escape come \n per quella materia. L'unico modo che ho trovato per farlo era inserire effettivamente il carattere di tabulazione nello script usando sed.

Detto questo, potresti prendere in considerazione l'utilizzo di Perl o Python. Ecco un breve script Python che ho scritto che io uso per tutti flusso regex'ing:

#!/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:]) 
+2

E la versione Perl sarebbe la shell one-liner "perl -pe 's/a/b /' filename" o " qualcosa | perl -pe 's/a/b /' " – tiftik

+0

Bello, è molto meglio :-) –

2

Non è necessario utilizzare sed per fare una sostituzione, quando in realtà, si vuole solo inserire una scheda di fronte della linea. La sostituzione di questo caso è un'operazione costosa rispetto alla semplice stampa, soprattutto quando si lavora con file di grandi dimensioni. È anche più facile da leggere in quanto non è regex.

ad esempio utilizzando awk

awk '{print "\t"$0}' $filename > temp && mv temp $filename 
32

Utilizzando Bash si può inserire un carattere di tabulazione a livello di codice in questo modo:

TAB=$'\t' 
echo 'line' | sed "s/.*/${TAB}&/g" 
echo 'line' | sed 's/.*/'"${TAB}"'&/g' # use of Bash string concatenation 
+0

Questo è molto utile. – Cheeso

+0

Eri sulla strada giusta con la "corda" $ ', ma senza una spiegazione. In effetti, sospetto, a causa dell'uso estremamente scomodo che probabilmente hai una comprensione incompleta (come la maggior parte di noi fa con bash). Vedere la mia spiegazione qui sotto: http://stackoverflow.com/a/43190120/117471 –

6

ho usato qualcosa di simile con una shell Bash su Ubuntu 12.04 (LTS) :

per appendere una nuova linea con scheda, secondo quando prima è abbinato:

sed -i '/first/a \\t second' filename 

Per sostituire primo con scheda, secondo:

sed -i 's/first/\\t second/g' filename 
+3

La doppia escape è la chiave, cioè usare '\\ t' e non' \ t'. – zamnuts

4

Usa $(echo '\t'). Avrai bisogno di virgolette sul modello.

Es. Per rimuovere una scheda:

sed "s/$(echo '\t')//" 
+3

È divertente che si stia utilizzando una funzione specifica di "GNU echo" (interpretando \ t come carattere di tabulazione) per risolvere un bug specifico di "BSD sed" (interpretando \ t come 2 caratteri separati). Presumibilmente, se hai "GNU echo" avresti anche "GNU sed". In tal caso non è necessario utilizzare l'eco. Con echo di BSD echo '\ t'' produrrà 2 caratteri separati. Il modo portatile POSIX è usare 'printf '\ t''. Questo è il motivo per cui dico: non cercare di rendere il tuo codice portatile non usando bash. È più difficile di quanto pensi. Usare 'bash' è la cosa più portabile che la maggior parte di noi possa fare. –

4

@sedit era sulla strada giusta, ma è un po 'scomodo per definire una variabile.

Solution (bash specifico)

Il modo per farlo in bash è quello di utilizzare mettere un simbolo del dollaro di fronte al vostro singola stringa tra apici.

$ 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 la stringa deve includere l'espansione variabile, è possibile ma citato stringhe in questo modo:

$ timestamp=$(date +%s) 
$ echo -e '1\n2\n3' | sed "s/.*/$timestamp"$'\t&/g' 
1491237958 1 
1491237958 2 
1491237958 3 

Spiegazione

In bash $'string' cause "espansione ANSI-C". E questo è ciò che la maggior parte di noi si aspettano quando usiamo le cose come \t, \r, \n, ecc Da: https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html#ANSI_002dC-Quoting

Parole del modulo $ 'stringa' sono trattati in modo speciale. La parola espande a stringa, con caratteri di escape rovesciato sostituiti come specificato da lo standard ANSI C. sequenze di escape backslash, se presenti, sono decodificati ...

Il risultato espanso è unico citato, come se il simbolo del dollaro non era presente.

Solution (se si deve evitare di bash)

io personalmente che la maggior parte gli sforzi per evitare bash sono stupido perché evitando bashismi NON * rendere il codice portabile. (Il tuo codice sarà meno fragile se lo condividi su bash -eu se cerchi di evitare bash e usare sh [a meno che tu non sia un assoluto POSIX ninja]). Ma piuttosto che avere un argomento religioso a riguardo, ti darò solo la migliore risposta *.

$ echo -e '1\n2\n3' | sed "s/.*/$(printf '\t')&/g" 
    1 
    2 
    3 

* MIGLIORE risposta? Sì, perché un esempio di ciò che la maggior parte degli script di shell anti-bash farebbe male nel loro codice è usare echo '\t' come in @robrecord's answer. Questo funzionerà con GNU echo, ma non con BSD echo. Ciò si spiega con il Gruppo Aperto a http://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html#tag_20_37_16

+0

La migliore soluzione, pazzesco non ha più upvotes. – scx

0

Invece di BSD sed, io uso perl:

[email protected]:~$ python -c "print('\t\t\thi')" |perl -0777pe "s/\t/ /g" 
    hi