Почему sed не распознает t как вкладку?


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

Я ожидаю, что этот скрипт sed вставит вкладку в шрифт каждой строки в $filename однако это не так. По какой-то причине он вставляет t вместо этого.. Странный..

10 75

10 ответов:

Не все версии sed понять \t. Просто вставьте литеральную вкладку вместо этого (нажмите Ctrl -V затем Tab).

С помощью Bash вы можете вставить символ табуляции программно следующим образом:

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

@седит был на правильном пути, но это немного неудобно, чтобы определить переменную.

решение (bash specific)

способ сделать это в bash-это использовать знак доллара перед вашей единственной строкой в кавычках.

$ 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

если ваша строка должна включать расширение переменной, вы можете поместить строки в кавычки вместе так:

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

объяснение

в bash $'string' вызывает "расширение ANSI-C". И это то, что большинство нас ожидают, когда мы используем такие вещи, как \t,\r,\n и т. д. От кого: https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html#ANSI_002dC-Quoting

слова форма $ 'string' рассматриваются специально. Слово расширяется к строка, С обратной косой чертой-экранированные символы заменяются, как указано стандарт ANSI C. Обратной косой черты escape-последовательности, если они есть, декодированный...

расширенный результат в одинарных кавычках, как если бы знак доллара не имел присутствовал.

решение (если вы должны избегать bash)

я лично думаю, что большинство попыток избежать bash глупы, потому что избегание башизмов не делает ваш код переносимым. (Ваш код будет менее хрупким, если вы shebang его в bash -eu чем если вы пытаетесь избежать bash и использовать sh [если вы не абсолютный POSIX ниндзя].) Но вместо того, чтобы иметь Религиозный аргумент об этом, Я просто дам вам лучший * ответ.

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

* лучший ответ? Да, потому что одним из примеров того, что большинство скриптеров оболочки anti-bash будут делать неправильно в своем коде, является use echo '\t' а в @robrecord это. Это будет работать для GNU echo, но не для BSD echo. Это объясняется открытой группой в http://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html#tag_20_37_16 и это пример того, почему попытки избежать башизмов обычно терпят неудачу.

я использовал что-то вроде этого с оболочкой Bash на Ubuntu 12.04 (LTS):

добавить новую строку с tab, второй, когда первый сочетается:

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

заменить первый С tab, второй:

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

использовать $(echo '\t'). Вам понадобятся кавычки вокруг шаблона.

например. Чтобы удалить вкладку:

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

вам не нужно использовать sed чтобы сделать замену, когда на самом деле, вы просто хотите вставить вкладку перед строкой. Замена для этого случая является дорогостоящей операцией по сравнению с просто распечаткой, особенно когда вы работаете с большими файлами. Его легче читать, а не регулярное выражение.

например, с помощью awk

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

sed не поддерживает \t, ни другие escape-последовательности, такие как \n если уж на то пошло. Единственный способ, который я нашел, чтобы сделать это было на самом деле вставить символ вкладки в скрипт с помощью sed.

тем не менее, вы можете рассмотреть возможность использования Perl или Python. Вот короткий скрипт Python, который я написал, который я использую для всех потоков 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:])

я использовал это на Mac:-

sed -i '' $'$i\\n\\thello\n' filename

использовать эту ссылку для справки

вместо BSD sed я использую perl:

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

Я думаю, что другие разъяснили это адекватно для других подходов (sed,AWK и т. д.). Однако, мой bash-конкретные ответы (протестированы на macOS High Sierra и CentOS 6/7) следуют.

1) Если OP хотел использовать метод поиска и замены, аналогичный тому, что они первоначально предложили, то я бы предложил использовать perl для этого, как следует. Примечания: обратные косые черты перед скобками для регулярного выражения не должны быть необходимы, и эта строка кода отражает, как лучше использовать, чем С perl оператор подстановки (например, per Perl 5 документация).

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

2) однако, как указал ghostdog74, так как желаемая операция на самом деле просто добавить вкладку в начале каждой строки перед изменением файла tmp на ввод/целевой файл ($filename), Я бы порекомендовал perl снова, но со следующими изменениями(ы):

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

3) Конечно, tmp файл лишние, так что лучше просто сделать все "на месте" (добавление -i флаг) и упростить вещи к более элегантному ОДН-вкладышу с

perl -i -pe $'s/^/\t/' $filename