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'
але не вистачало пояснень. Насправді я підозрюю, що через надзвичайно незручне використання ви, мабуть, маєте неповне розуміння (як це робить більшість із нас з басом). Дивіться моє пояснення нижче: stackoverflow.com/a/43190120/117471
$TAB
усередині одиничних лапок, тому вам потрібно буде використовувати його подвійні лапки.
*
внутрішніх подвійних лапок ... це трактуватиметься як глобус, а не як регулярний вираз.
@sedit був на правильному шляху, але визначити змінну трохи незручно.
Спосіб зробити це в баші - поставити знак долара перед вашою єдиною котируваною струною.
$ 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. Послідовності втечі зворотного схилу, якщо вони є, декодуються ...
Розширений результат одноцитований, як ніби знак долара не був.
Я особисто думаю, що більшість зусиль, щоб уникнути баш, нерозумні, оскільки уникнення башизмів НЕ * робить ваш код портативним. (Ваш код буде менш крихким, якщо ви його шибангові, bash -eu
ніж якщо ви намагаєтеся уникати баш і використовувати sh
[якщо ви не абсолютний ніндзя POSIX]). * відповідь.
$ echo -e '1\n2\n3' | sed "s/.*/$(printf '\t')&/g"
1
2
3
* НАЙКРАЩА відповідь? Так, тому що одним із прикладів того, що більшість скриптерів проти бомбних оболонок зробить неправильно у своєму коді, є використання, echo '\t'
як у відповіді @ robrecord . Це буде працювати для відлуння GNU, але не для BSD. Це пояснюється 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
, ані інші послідовності втечі, як \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) Якщо ОП хотів би використовувати метод пошуку та заміни, подібний до того, що вони спочатку пропонували, то я б запропонував використовувати perl
для цього наступне. Примітки: зворотні риски перед дужками для регулярного вираження не повинні бути необхідними, і цей рядок коду відображає, як $1
краще використовувати, ніж \1
з perl
оператором заміни (наприклад, в документації Perl 5 ).
perl -pe 's/(.*)/\t$1/' $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