sed "s/\(.*\)/\t\1/" $filename > $sedTmpFile && mv $sedTmpFile $filename
Oczekuję, że ten sed
skrypt wstawi znak tab
przed każdym wierszem, $filename
ale tak nie jest. Z jakiegoś powodu t
zamiast tego wstawia .
sed "s/\(.*\)/\t\1/" $filename > $sedTmpFile && mv $sedTmpFile $filename
Oczekuję, że ten sed
skrypt wstawi znak tab
przed każdym wierszem, $filename
ale tak nie jest. Z jakiegoś powodu t
zamiast tego wstawia .
Odpowiedzi:
Nie wszystkie wersje sed
rozumieją \t
. Po prostu wstaw zamiast tego dosłowną tabulator (naciśnij Ctrl- Va następnie Tab).
\t
w części zamiennej wyrażenia (rozpoznano ją \t
w części pasującej do wzorca)
Używając Bash możesz wstawić znak TAB programowo, tak jak poniżej:
TAB=$'\t'
echo 'line' | sed "s/.*/${TAB}&/g"
echo 'line' | sed 's/.*/'"${TAB}"'&/g' # use of Bash string concatenation
$'string'
wyjaśnieniem, ale brakuje ci. W rzeczywistości podejrzewam, ze względu na wyjątkowo niewygodne użycie, które prawdopodobnie masz niepełne zrozumienie (jak większość z nas robi z bashem). Zobacz moje wyjaśnienie poniżej: stackoverflow.com/a/43190120/117471
$TAB
wewnątrz pojedynczych cudzysłowów, więc będziesz musiał używać go w podwójnych cudzysłowach.
*
podwójnych cudzysłowów ... będzie to traktowane jako glob, a nie jako wyrażenie regularne, które zamierzasz.
@sedit był na właściwej ścieżce, ale definiowanie zmiennej jest trochę niewygodne.
Sposobem na zrobienie tego w bash jest umieszczenie znaku dolara przed łańcuchem w pojedynczym cudzysłowie.
$ 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
Jeśli twój ciąg musi zawierać rozwinięcie zmiennych, możesz umieścić łańcuchy w cudzysłowie w następujący sposób:
$ timestamp=$(date +%s)
$ echo -e '1\n2\n3' | sed "s/.*/$timestamp"$'\t&/g'
1491237958 1
1491237958 2
1491237958 3
W bash $'string'
powoduje "rozszerzenie ANSI-C". I to jest to, co większość z nas spodziewać, gdy używamy takich rzeczy \t
, \r
, \n
, itd. Od: https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html#ANSI_002dC-Quoting
Słowa w postaci $ 'string' są traktowane specjalnie. Słowo jest interpretowane jako łańcuch , a znaki ze znakami ucieczki odwrotnym ukośnikiem są zastępowane zgodnie ze standardem ANSI C. Sekwencje ucieczki z ukośnikiem odwrotnym, jeśli są obecne, są dekodowane ...
Rozszerzony wynik jest podawany w apostrofy, tak jakby znak dolara nie był obecny.
Osobiście uważam, że większość wysiłków mających na celu uniknięcie basha jest głupia, ponieważ unikanie bashizmu NIE * sprawia, że kod jest przenośny. (Twój kod będzie mniej kruchy, jeśli go uderzysz, bash -eu
niż jeśli spróbujesz uniknąć basha i użyjesz sh
[chyba że jesteś absolutnym ninja POSIX].) Ale zamiast dyskutować na ten temat religijnie, dam ci tylko NAJLEPSZE * odpowiedź.
$ echo -e '1\n2\n3' | sed "s/.*/$(printf '\t')&/g"
1
2
3
* Najlepsza odpowiedź? Tak, ponieważ jednym z przykładów tego, co większość skryptów powłoki anty-bash zrobiłaby źle w swoim kodzie, jest użycie echo '\t'
tak, jak w odpowiedzi @ robrecord . To zadziała dla echa GNU, ale nie echa BSD. Wyjaśnia to The Open Group na http://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html#tag_20_37_16 I to jest przykład dlaczego próby uniknięcia bashizmów zwykle zawodzą.
Użyłem czegoś takiego z powłoką Bash na Ubuntu 12.04 (LTS):
Aby dołączyć nową linię z tabulatorem, druga po dopasowaniu pierwszej :
sed -i '/first/a \\t second' filename
Aby wymienić pierwsze z zakładki, po drugie :
sed -i 's/first/\\t second/g' filename
\\t
a nie \t
.
Posługiwać się $(echo '\t')
. Będziesz potrzebować cytatów wokół wzoru.
Na przykład. Aby usunąć kartę:
sed "s/$(echo '\t')//"
echo '\t'
2 oddzielne znaki. Przenośnym sposobem POSIX jest użycie printf '\t'
. Dlatego mówię: nie próbuj przenosić swojego kodu, nie używając basha. To trudniejsze niż myślisz. Używanie bash
jest najbardziej przenośną rzeczą, jaką większość z nas może zrobić.
Nie musisz używać sed
do podstawiania, gdy w rzeczywistości chcesz po prostu wstawić tabulator przed linią. Zastępowanie w tym przypadku jest kosztowną operacją w porównaniu do zwykłego drukowania, zwłaszcza podczas pracy z dużymi plikami. Jest też łatwiejszy do odczytania, ponieważ nie zawiera wyrażenia regularnego.
np. używając awk
awk '{print "\t"$0}' $filename > temp && mv temp $filename
Użyłem tego na Macu:
sed -i '' $'$i\\\n\\\thello\n' filename
sed
nie obsługuje \t
, ani innych sekwencji ucieczki, jak \n
w tym przypadku. Jedynym sposobem, w jaki udało mi się to zrobić, było wstawienie znaku tabulacji do skryptu za pomocąsed
.
To powiedziawszy, możesz rozważyć użycie Perla lub Pythona. Oto krótki skrypt w Pythonie, który napisałem, i którego używam do wszystkich regexów strumieniowych:
#!/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:])
Zamiast seda z BSD używam perla:
ct@MBA45:~$ python -c "print('\t\t\thi')" |perl -0777pe "s/\t/ /g"
hi
Myślę, że inni wyjaśnić to odpowiednio do innych metod ( sed
, AWK
, itd.). Jednak moje bash
odpowiedzi specyficzne (przetestowane na macOS High Sierra i CentOS 6/7) są dalej.
1) Jeśli OP chciałby użyć metody wyszukiwania i zamiany podobnej do tej, którą pierwotnie zaproponowali, sugerowałbym użycie perl
do tego w następujący sposób. Uwagi: ukośniki odwrotne przed nawiasami dla wyrażenia regularnego nie powinny być konieczne, a ta linia kodu odzwierciedla, w jaki sposób $1
lepiej jest używać niż \1
z perl
operatorem podstawienia (np. W dokumentacji Perl 5 ).
perl -pe 's/(.*)/\t$1/' $filename > $sedTmpFile && mv $sedTmpFile $filename
2) Jednak, jak wskazał ghostdog74 , ponieważ pożądaną operacją jest po prostu dodanie tabulatora na początku każdego wiersza przed zmianą pliku tmp na plik wejściowy / docelowy ( $filename
), polecam perl
ponownie, ale z następującą modyfikacją (s):
perl -pe 's/^/\t/' $filename > $sedTmpFile && mv $sedTmpFile $filename
## OR
perl -pe $'s/^/\t/' $filename > $sedTmpFile && mv $sedTmpFile $filename
3) Oczywiście plik tmp jest zbędny , więc lepiej po prostu zrobić wszystko `` na miejscu '' (dodając -i
flagę) i uprościć wszystko do bardziej eleganckiej jednowierszowej
perl -i -pe $'s/^/\t/' $filename