Prosta wymiana kart w tajemniczy sposób zawodzi


44

To powinno być naprawdę proste, ale z jakiegoś powodu nie działa:

sed -i.bak -E 's/\t/  /' file.txt

Zamiast zastępować znaki tabulacji, zastępuje tznaki. Wypróbowałem każdą odmianę, o której mogłem pomyśleć, bawiąc się cytowaniem itp. Poszukałem google i znalazłem, że wszyscy używają podobnych wyrażeń i wydaje się, że działają dla nich.

To -Ejest OS X. Myślałem, że niepowodzenie może być wynikiem dziwnego dziwactwa OS X sed, więc wypróbowałem to również z Ruby (bez -i) i uzyskałem ten sam wynik:

ruby -pe '$_.gsub!(/\t/,"  ")' < file.txt > file.new

Używam Bash 3.2.51 na OS X i iTerm, chociaż nie widzę, jak którekolwiek z nich może być bardzo istotne. Nie ustawiłem żadnych dziwnych zmiennych środowiskowych, choć mogę opublikować dowolne, które Twoim zdaniem mogą być istotne.

Co może być nie tak?

UPDATE : Muszę dokonały jakiś inny błąd lub literówka kiedy próbowałem wersji Ruby, ponieważ Gilles zaznacza, że robi pracę (a ja nigdy nie miałem go skierować mnie źle!). Nie jestem pewien, co się stało, ale jestem prawie pewien, że to był mój błąd.


5
Być może powinieneś spróbować zastąpić instrukcję \tw miejscu sed, w CTRL-V<TAB>którym <TAB>znajduje się klawisz Tab i CTRL-Vklawisz Control i vnaciśnięcie razem.
unxnut

jeśli Ruby również otrzymuje błędną odpowiedź, może to być Twoja biblioteka wyrażeń regularnych. (Przetestowałem oba twoje polecenia i oba zastąpiły tabulację 2 spacjami.) Więc mam nadzieję, że jeśli zainstalujesz Gnu sed, zainstaluje również poprawną bibliotekę.
ctrl-alt-delor

Odpowiedzi:


64

Składnia \tznaku tabulacji w sed nie jest standardowa. Ta ucieczka jest rozszerzeniem GNU sed . W Internecie znajduje się wiele przykładów, które go używają, ponieważ wiele osób korzysta z GNU sed (jest to implementacja sed w niewbudowanym systemie Linux). Ale OS X sed , podobnie jak inne * BSD sed, nie obsługuje \ttab, a zamiast tego traktuje to \tjako ukośnik odwrotny t.

Istnieje wiele rozwiązań, takich jak:

  • Użyj dosłownego znaku tabulacji.

    sed -i.bak 's/  /  /' file.txt
    
  • Użyj trlub, printfaby utworzyć znak tabulacji.

    sed -i.bak "s/$(printf '\t')/  /" file.txt
    sed -i.bak "s/$(echo a | tr 'a' '\t')/  /" file.txt
    
  • Użyj składni ciągu bash, umożliwiając ucieczki odwrotnym ukośnikiem .

    sed -i.bak $'s/\t/  /' file.txt
    
  • Użyj Perla, Pythona lub Ruby. Opublikowany fragment kodu Ruby działa.


W przypadku skryptów sed zawartych w ...sedskrypcie (używanych przez -fopcję) dosłowne znaki tabulacji wydają mi się jedyną możliwością. Podczas edycji tego za pomocą vima set noexpandtabważne jest.
Tobias

Ostrzeżenie: Użyj tej techniki „dosłownego znaku tabulacji” tylko wtedy, gdy chcesz, aby twój współpracownik wrócił za tobą i złamał skrypt później. Zastosuj tę trtechnikę tylko wtedy, gdy chcesz, aby twój współpracownik dźgnął cię w twarz podczas czytania twojego scenariusza.
Bruno Bronosky,

Czy drugi znak podwójnego cudzysłowu jest umieszczony w drugim bloku kodu? Musiałem przenieść go tam, gdzie obecnie znajduje się końcowy cytat.
Ellen Spertus

Dzięki za link do składni ciągu bash ... Nie miałem pojęcia (i to jest najlepsza opcja, IMHO).
levigroker

sed $'s/<regex>/\t/' file.txtdziała na wstawianie, ale $wydaje się, że łamie mój skrypt, gdy próbuję dołączyć część wyrażenia regularnego do mojego podstawienia, tj. sed $'s,\(ontology/[0-9]\+\),\t\txxx\1xxx\t\t,'daje `xxxxxx` z moją oczekiwaną wartością dopasowania zastąpioną przez ``. Czy istnieje odpowiednik \1przy użyciu składni łańcucha bash? Edycja: w środku xxx <U + 231C> xxx należy umieścić znak Unicode U + 231C.
Josh

14

Użyj cytowania specyficznego dla Bash, które pozwala ci używać ciągów jak w C, aby prawdziwy znak tab był przekazywany do sed, a nie sekwencja ucieczki:

sed -i.bak -E $'s/\t/  /' file.txt

1
Nazywany także „ANSI-C”, jeśli inni chcą znaleźć więcej informacji na ten temat.
wisbucky

2
Wygląda na to, że działa na dowolnej powłoce bourne, działa także na UNIX'ach innych niż bash. Nie działa jednak na wariantach csh.
jornane

2
sed -i $'s/\t/  /g' file.txt 

działa dla mnie na OS X i jest to ta sama komenda, której używam przez cały czas w systemie Linux.


Zauważ, że to zastępuje wszystkie zakładki w każdym rzędzie, podczas gdy OP zamierza zastąpić tylko pierwszy (sądząc po poleceniu, którego używają).
Kusalananda

1

Jak wspomniano, nie wszystkie sedimplementacje obsługują zapis \tjako kartę poziomą.

Możesz łatwo osiągnąć swoją zamianę za pomocą:

 perl -pi.old -e 's{\t+}{ }g' file.txt

Wykonuje to zamianę in situ, która zachowuje oryginalny plik jako „* .old”. Perl pozwala na stosowanie alternatywnych ograniczników dla klasyki, /dzięki czemu wyrażenie jest znacznie bardziej czytelne (tj. Pozbawione syndromu „pochylonej wykałaczki”).

+Mówi jeden lub więcej powtórzeń o charakterze zakładki mają być zastąpione. gModyfikator umożliwia globalne zamienniki całej końcu każdej linii.


0

Możesz także użyć echowewnątrz sed:

sed -i "s/$(echo '\t')//g"


Zauważ, że echo '\t'po prostu wyświetli się \tw implementacji niektórych powłok echo.
Kusalananda

0

Jeśli chcesz mieć większą moc sed(wsparcie \ti więcej) niż ten w OS X, zainstaluj GNU sed .


Ponieważ nie działało to również z Ruby, nie jestem pewien, dlaczego miałbym doszedł do wniosku, że OS X sedjest problemem. Czy masz powód, by sądzić, że to jest problem? Z przyjemnością zainstalowałbym GNU sed, gdybym miał powód, by sądzić, że to rozwiąże problem, ale wygląda na to, że prawie to wykluczyłem.
iconoclast

Z Ruby będziesz musiał użyć tylko jednego odwrotnego ukośnika:ruby -pe '$_.gsub!(/\t/," ")' < file.txt
vinc17

0

Jeśli wymaganie bashlub zshpowłoka jest w porządku , to jest to najłatwiejsze rozwiązanie, jakie mogę wymyślić:

sed "s/$(echo -n -e "\t")/ /" file.txt

Zauważ jednak, że echoflagi ( -ni -e) są niezdefiniowane w POSIX, więc powłoka zgodna z POSIX nie wymaga zrozumienia tych flag, ale wiele z nich ze względu na kompatybilność.


-1

Dziwi mnie, że nikt nie zasugerował bardzo prostego rozwiązania: sed -i.bak -E 's/\\\t/ /' file.txt To powinno wystarczyć.

Musisz uciec przed ucieczką (stąd 3 \ s), aby sed mógł zrozumieć, że próbujesz użyć znaku \ t w wyrażeniu regularnym, gdy wszystko zostanie zastąpione ...


Dlaczego właśnie trzy odwrotne ukośniki?
Michael Homer

3
Jeśli używam GNU sed, jeden \ wystarczy, jak ma ucieczki jest konieczne. Problem polega na tym, że BSD sednie obsługuje tej składni dla kart.
iconoclast

Nie działa na moim El Capitan.
Franklin Yu,

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.