sed "s/\(.*\)/\t\1/" $filename > $sedTmpFile && mv $sedTmpFile $filename
Я ожидаю, что этот sed
скрипт вставит tab
перед каждой строкой, $filename
но это не так. По какой-то причине t
вместо него вставляется .
sed "s/\(.*\)/\t\1/" $filename > $sedTmpFile && mv $sedTmpFile $filename
Я ожидаю, что этот sed
скрипт вставит tab
перед каждой строкой, $filename
но это не так. По какой-то причине t
вместо него вставляется .
Ответы:
Не все версии sed
понимают \t
. Вместо этого просто вставьте буквальную табуляцию (нажмите Ctrl- Vзатем Tab).
\t
заменяющую часть выражения (она \t
прекрасно распознается в части сопоставления с образцом)
Используя Bash, вы можете программно вставить символ TAB следующим образом:
TAB=$'\t'
echo 'line' | sed "s/.*/${TAB}&/g"
echo 'line' | sed 's/.*/'"${TAB}"'&/g' # use of Bash string concatenation
$'string'
но без объяснения причин. На самом деле я подозреваю, что из-за чрезвычайно неудобного использования вы, вероятно, не полностью понимаете это (как большинство из нас делает с bash). См. Мое объяснение ниже: stackoverflow.com/a/43190120/117471
$TAB
внутри одинарных кавычек, поэтому вам нужно использовать двойные кавычки.
*
внутри двойных кавычек ... это будет рассматриваться как глобус, а не как регулярное выражение, которое вы намереваетесь.
@sedit был на правильном пути, но определять переменную немного неудобно.
В 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
Особо обрабатываются слова вида $ 'строка' . Слово расширяется до строки заменяется с заменой экранированных символов обратной косой черты, как указано в стандарте ANSI C. Управляющие последовательности с обратной косой чертой, если они есть, декодируются ...
Расширенный результат заключен в одинарные кавычки, как если бы знака доллара не было.
Я лично считаю, что большинство попыток избежать bash глупо, потому что избегание bashisms НЕ * делает ваш код переносимым. (Ваш код будет менее хрупким, если вы его измените, bash -eu
чем если вы попытаетесь избежать bash и использовать sh
[если вы не являетесь абсолютным ниндзя POSIX].) Но вместо того, чтобы иметь религиозные аргументы по этому поводу, я просто дам вам НАИЛУЧШЕЕ * ответ.
$ echo -e '1\n2\n3' | sed "s/.*/$(printf '\t')&/g"
1
2
3
* Лучший ответ? Да, потому что один из примеров того, что большинство сценариев оболочки анти-bash сделали бы неправильно в своем коде, - это использование, echo '\t'
как в ответе @ robrecord . Это будет работать для GNU echo, но не для BSD echo. Это объясняет The Open Group на http://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html#tag_20_37_16. И это пример того, почему попытки избежать башизмов обычно терпят неудачу.
Я использовал что-то подобное с оболочкой Bash в Ubuntu 12.04 (LTS):
Чтобы добавить новую строку с табуляцией, вторая при совпадении первой :
sed -i '/first/a \\t second' filename
Чтобы заменить первое на вкладку, второе :
sed -i 's/first/\\t second/g' filename
\\t
а не использование \t
.
Используйте $(echo '\t')
. Вам понадобятся цитаты вокруг выкройки.
Например. Чтобы удалить вкладку:
sed "s/$(echo '\t')//"
echo '\t'
выводит 2 отдельных символа. Переносимый способ POSIX - использовать printf '\t'
. Вот почему я говорю: не пытайтесь сделать свой код переносимым, не используя bash. Это сложнее, чем ты думаешь. Использование bash
- это самая портативная вещь, которую может сделать большинство из нас.
Вам не нужно использовать sed
замену, когда на самом деле вы просто хотите вставить табуляцию перед строкой. Замена в этом случае - дорогостоящая операция по сравнению с простой распечаткой, особенно когда вы работаете с большими файлами. Его тоже легче читать, так как это не регулярное выражение.
например, используя awk
awk '{print "\t"$0}' $filename > temp && mv temp $filename
Я использовал это на Mac:
sed -i '' $'$i\\\n\\\thello\n' filename
sed
не поддерживает \t
, ни другие escape-последовательности, как \n
в этом отношении. Единственный способ, который я нашел для этого, - это вставить символ табуляции в скрипт, используяsed
.
Тем не менее, вы можете рассмотреть возможность использования Perl или Python. Вот небольшой сценарий Python, который я написал, который я использую для всех регулярных выражений потока:
#!/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:])
Вместо 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
для этого, как показано ниже. Примечания: обратная косая черта перед круглыми скобками для регулярного выражения не обязательна, и эта строка кода отражает, как $1
лучше использовать, чем \1
с perl
оператором подстановки (например, согласно документации Perl 5 ).
perl -pe 's/(.*)/\t$1/' $filename > $sedTmpFile && mv $sedTmpFile $filename
2) Однако, как указано ghostdog74 , поскольку желаемая операция на самом деле состоит в том, чтобы просто добавить вкладку в начале каждой строки перед изменением файла tmp во входной / целевой файл ( $filename
), я бы рекомендовал perl
снова, но со следующей модификацией (s):
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