Sed neden sekme olarak \ t tanımıyor?


106
sed "s/\(.*\)/\t\1/" $filename > $sedTmpFile && mv $sedTmpFile $filename

Bu sedbetiğin tabher satırın önüne bir eklemesini bekliyorum , $filenameancak öyle değil. Bazı nedenlerden dolayı onun tyerine a ekliyor.


1
Sed, platformlar arasında değişiklik gösterebileceğinden (özellikle, BSD / MacOSX'e karşı Linux), sed'i kullandığınız platformu belirtmeniz faydalı olabilir.
Isaac

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

OS X (macOS) kullanıcısı için bu soruya bakın .
Franklin Yu

Yanıtlar:


129

Tüm sedanlayış sürümleri değil \t. Bunun yerine hazır bilgi sekmesi eklemeniz yeterlidir ( Ctrl- Vardından - tuşuna basın Tab).


2
Ah evet; netleştirmek için: sed değil tüm sürümleri anlamaya \tifade yedek parçası (tanınan \tgayet desen eşleştirme kısmen)
John Weldon

3
awwwwwwwwwwwwwwwwwww, tamam bu oldukça ilginç. Ve tuhaf. Neden onu bir yerde tanımasını sağladın ama diğerinde değil ...?
sixtyfootersdude

2
Bir komut dosyasından çağrılan, çalışmaz: sekmeler sh tarafından yok sayılır. Örneğin, bir kabuk komut dosyasından alınan aşağıdaki kod, bir tabülasyonla başına eklenmeden $ TEXT_TO_ADD ekler: sed "$ {LINE} a \\ $ TEXT_TO_ADD" $ FILE.
Dereckson

2
@Dereckson ve diğerleri - bu yanıta bakın: stackoverflow.com/a/2623007/48082
Cheeso

2
Dereckson un / can / can't /?
Douglas

41

Bash'i kullanarak programlı olarak bir SEKME karakteri ekleyebilirsiniz:

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

Bu çok yardımcı.
Cheeso

1
Sen ile doğru yolda $'string'ama eksikliği açıklama. Aslında, son derece garip kullanım nedeniyle muhtemelen eksik bir anlayışa sahip olduğunuzdan şüpheleniyorum (çoğumuz bash ile yaptığımız gibi). Aşağıdaki açıklamama bakın: stackoverflow.com/a/43190120/117471
Bruno Bronosky

1
BASH'in $TABtek tırnak içindeki gibi değişkenleri genişletmeyeceğini unutmayın , bu nedenle çift tırnak kullanmanız gerekecek.
nealmcb

*Çift tırnak içinde kullanmaya dikkat edin ... bu, amaçladığınız normal ifade olarak değil, bir küre olarak değerlendirilecektir.
levigroker

28

@sedit doğru yoldaydı, ancak bir değişkeni tanımlamak biraz garip.

Çözüm (bash'a özel)

Bunu bash'ta yapmanın yolu, tek tırnaklı dizenizin önüne bir dolar işareti koymaktır.

$ 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

Dizenizin değişken genişletme içermesi gerekiyorsa, tırnak içine alınmış dizeleri şu şekilde bir araya getirebilirsiniz:

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

Açıklama

Bash'de $'string'"ANSI-C genişlemesine" neden olur. Ve bu gibi şeyler kullandığınızda çoğumuz ne bekliyordunuz \t, \r, \n: Dan vb https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html#ANSI_002dC-Quoting

$ 'Dizge' biçimindeki sözcükler özel olarak ele alınır. Sözcük , ANSI C standardında belirtildiği gibi ters eğik çizgi kaçış karakterleri değiştirilerek dizeye genişler . Varsa ters eğik çizgi kaçış dizilerinin kodu çözülür ...

Genişletilmiş sonuç sanki dolar işareti yokmuş gibi tek tırnaklıdır.

Çözüm (bash'tan kaçınmanız gerekiyorsa)

Şahsen, bash'ı önlemek için gösterilen çabaların çoğunun aptalca olduğunu düşünüyorum çünkü bashizmlerden kaçınmak kodunuzu taşınabilir hale getirmez *. (Kodunuz, bash'dan bash -eukaçınmaya ve sh[mutlak bir POSIX ninja değilseniz] kullanmaya çalıştığınızda olduğundan daha az kırılgan olacaktır .) Ancak bununla ilgili dini bir argüman yerine, size EN İYİSİNİ vereceğim * Cevap.

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

* En iyi cevap? Evet, çünkü çoğu anti-bash kabuk betikleyicisinin kodlarında yanlış yapacaklarına bir örnek @ robrecord'un cevabındakiecho '\t' gibi kullanmaktır . Bu GNU yankısı için çalışacak, ancak BSD yankısı için çalışmayacaktır. Bu, Açık Grup tarafından http://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html#tag_20_37_16 adresinde açıklanmaktadır Ve bu, bashizmlerden kaçınmaya çalışmanın neden genellikle başarısız olduğunun bir örneğidir.


8

Ubuntu 12.04'te (LTS) bir Bash kabuğu ile buna benzer bir şey kullandım:

İle yeni bir satır eklemek için sekme, ikinci zaman ilk eşleştirilir:

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

Değiştirmek için ilk olan sekme saniyede :

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

4
Çift kaçış anahtardır, yani kullanın \\tve kullanmayın \t.
zamnuts

Ayrıca Ubuntu 16.04 ve Bash 4.3'te tek tırnak yerine çift tırnak kullanmak zorunda kaldım.
2019

4

Kullanın $(echo '\t'). Modelin etrafında alıntılara ihtiyacınız olacak.

Örneğin. Bir sekmeyi kaldırmak için:

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

5
"BSD sed" 'e özgü bir hatayı çözmek için (\ t'yi 2 ayrı karakter olarak yorumlayarak) "GNU echo" özelliğine özgü bir özellik (\ t'yi bir sekme karakteri olarak yorumlayarak) kullanıyor olmanız komik. Muhtemelen, eğer "GNU yankı" na sahipseniz, "GNU sed" e de sahip olacaksınız. Bu durumda yankı kullanmanıza gerek kalmaz. BSD yankısı echo '\t'ile 2 ayrı karakter çıkacaktır. POSIX taşınabilir yolu kullanmaktır printf '\t'. Bu yüzden şunu söylüyorum: Bash kullanmayarak kodunuzu taşınabilir hale getirmeye çalışmayın. Düşündüğünden daha zor. Kullanmak bash, çoğumuzun yapabileceği en taşınabilir şeydir.
Bruno Bronosky

3

sedGerçekte, sadece satırın önüne bir sekme eklemek istediğinizde, bir değişiklik yapmak için kullanmanıza gerek yoktur. Bu durum için ikame, özellikle büyük dosyalarla çalışırken, yalnızca yazdırmaya kıyasla pahalı bir işlemdir. Normal ifade olmadığı için okunması daha kolaydır.

örneğin awk kullanarak

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


0

seddesteklemiyor \tveya \nbunun gibi başka kaçış dizileri . Bunu yapmanın tek yolu, kullanarak komut dosyasına sekme karakterini eklemekti sed.

Bununla birlikte, Perl veya Python kullanmayı düşünebilirsiniz. İşte tüm akış düzenli ifadeleri için kullandığım yazdığım kısa bir Python betiği:

#!/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
Ve Perl sürümü, kabuk tek satırlık "perl -pe 's / a / b /' dosya adı" veya "bir şey | perl -pe 's / a / b /" "
tiftik

0

BSD sed yerine perl kullanıyorum:

ct@MBA45:~$ python -c "print('\t\t\thi')" |perl -0777pe "s/\t/ /g"
   hi

0

Diğerleri diğer yaklaşımlar (için yeterince bu açıklık düşünüyorum sed, AWKvs.). Bununla birlikte, benim özel bashcevaplar (macOS High Sierra ve CentOS 6 / 7'de test edilmiştir) bunu takip eder.

1) OP, başlangıçta önerdiklerine benzer bir ara ve değiştir yöntemi kullanmak isterse perl, bunun için aşağıdaki şekilde kullanılmasını öneririm . Notlar: Düzenli ifade için parantezlerin önündeki ters eğik çizgiler gerekli olmamalıdır ve bu kod satırı , ikame operatöründen (örneğin Perl 5 dokümantasyonu uyarınca ) $1kullanımının nasıl daha iyi olduğunu gösterir .\1perl

perl -pe 's/(.*)/\t$1/' $filename > $sedTmpFile && mv $sedTmpFile $filename

2) Bununla birlikte, ghostdog74 tarafından belirtildiği gibi , istenen işlem aslında tmp dosyasını giriş / hedef dosyasına ( ) değiştirmeden önce her satırın başına basitçe bir sekme eklemek olduğundan, tekrar $filenametavsiye ederim, perlancak aşağıdaki değişiklik ile (ler):

perl -pe 's/^/\t/' $filename > $sedTmpFile && mv $sedTmpFile $filename
## OR
perl -pe $'s/^/\t/' $filename > $sedTmpFile && mv $sedTmpFile $filename

3) Elbette, tmp dosyası gereksizdir , bu nedenle her şeyi 'yerinde' yapmak ( -ibayrak ekleyerek ) ve işleri daha zarif bir tek satıra basitleştirmek daha iyidir.

perl -i -pe $'s/^/\t/' $filename
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.