sed in-place flag, який працює як на Mac (BSD), так і на Linux


237

Чи існує виклик sedредагування на місці без резервного копіювання, яке працює як на Linux, так і на Mac? Хоча BSD, що sedпостачається з OS X, здається, потребує sed -i '' …, sedдистрибутиви GNU Linux зазвичай поставляються з інтерпретацією лапок як порожнє ім'я вхідного файлу (замість розширення резервного копіювання), а потребує sed -i …цього.

Чи є синтаксис командного рядка, який працює з обома ароматами, тому я можу використовувати один і той же скрипт в обох системах?


Чи perl не є варіантом? Потрібно використовувати sed?
Нуфал Ібрагім

Можливо, встановіть версію GNU і використовуйте це! topbug.net/blog/2013/04/14/… ? Я спробую це спершу. Потім знову такий вид смокче.
Дмитро Міньковський

2
@dimadima: Може бути цікавим іншим людям, які переглядають це питання, які мають особисті сценарії, які розбиваються на їх ОС OS X У моєму випадку, мені це знадобилося для системи збирання проекту з відкритим кодом, де сповіщення вашого користувача, щоб спочатку встановити GNU sed, перемогло б початкову мету цієї вправи (закріпіть кілька файлів способом "працює скрізь") .
dnadlinger

@klickverbot так, має сенс. Я спершу додав коментар як відповідь, а потім видалив його, зрозумівши, що це не відповідь на ваше запитання :).
Дмитро Мінковський,

1
Перехресне посилання: Як досягти портативності за допомогою sed -i (на місці редагування)? на обмін стеками Unix & Linux.
MvG

Відповіді:


212

Якщо ви дійсно хочете просто скористатися sed -i«простим» способом, наступне НЕ працює на GNU та BSD / Mac sed:

sed -i.bak 's/foo/bar/' filename

Зверніть увагу на брак місця і крапки.

Доказ:

# GNU sed
% sed --version | head -1
GNU sed version 4.2.1
% echo 'foo' > file
% sed -i.bak 's/foo/bar/' ./file
% ls
file  file.bak
% cat ./file
bar

# BSD sed
% sed --version 2>&1 | head -1
sed: illegal option -- -
% echo 'foo' > file
% sed -i.bak 's/foo/bar/' ./file
% ls
file  file.bak
% cat ./file
bar

Очевидно, ви могли потім просто видалити .bakфайли.


2
Це шлях. Додайте кілька символів, і це портативно. Видалення файлу резервної копії є тривіальним (у вас уже є ім'я файлу при виклику sed)
slezica

Це працювало на mac, але на ubuntu 16.04 він замінює вихідний файл на 0 байт і створює .bak файл також з 0 байтами?
будинок9

1
Слід зазначити, що на MacOS Sierra (і , можливо , раніше, я не маю машину під рукою), sedне приймає позиційне commandаргумент при виклику з -i- він повинен бути забезпечений іншим прапором, -e. Таким чином, команда стає:sed -i.bak -e 's/foo/bar/' filename
ELLIOTTCABLE

5
Це створює резервні копії, в той час як ОП спеціально вимагає редагування на місці.
Марк-Андре Лафортун

8
Тож повне рішення:sed -i.bak 's/foo/bar/' file && rm file.bak
joeytwiddle

112

Це працює з GNU sed, але не в OS X:

sed -i -e 's/foo/bar/' target.file
sed -i'' -e 's/foo/bar/' target.file

Це працює в OS X, але не з GNU sed:

sed -i '' -e 's/foo/bar/' target.file

На OS X ви

  • не вдається використовувати, sed -i -eоскільки розширення файлу резервної копії буде встановлено на-e
  • не може використовуватись sed -i'' -eз тих же причин - для цього потрібен пробіл між -iі ''.

1
@AlexanderMills Повідомляє, що наступним аргументом є команда - тут також можна пропустити.
Бенджамін В.

4
Зауважте, що -iі -i''є ідентичними під час розбору оболонки; sed не може поводитись по-різному на цих двох перших викликах, тому що отримує абсолютно однакові аргументи.
wchargin

44

Коли в OSX, я завжди встановлюю версію sed GNU через Homebrew, щоб уникнути проблем у сценаріях, оскільки більшість сценаріїв були написані для версій sed GNU.

brew install gnu-sed --with-default-names

Тоді ваш BSD sed буде замінений GNU sed.

Крім того, ви можете встановити без імен за замовчуванням, але потім:

  • PATHПісля встановлення змініть свою інструкціюgnu-sed
  • Перевірте у своїх сценаріях, щоб вибрати, gsedчи не sedзалежно від вашої системи

4
--with-default-namesбуло видалено з доморощеного-ядра , більше інформації в цьому відповіді. при встановленні gnu-sedзараз, інструкція по встановленню вказує, що потрібно додати gnubinдо свого PATH:PATH="/usr/local/opt/gnu-sed/libexec/gnubin:$PATH"
pgericson

18

Як запитує Нуфал Ібрагім , чому ви не можете використовувати Perl? Будь-який Mac матиме Perl, і є дуже мало дистрибутивів Linux або BSD, які не містять певної версії Perl в базовій системі. Одним з єдиних середовищ, у яких фактично може бути відсутність Perl, буде BusyBox (який працює як GNU / Linux для -i, за винятком того, що не можна вказати розширення резервного копіювання).

Як рекомендує ismail ,

Оскільки Perl доступний скрізь, де я просто займаюся perl -pi -e s,foo,bar,g target.file

і це здається кращим рішенням майже в будь-якому випадку, ніж сценарії, псевдоніми та інші способи вирішення принципової несумісності sed -iміж GNU / Linux та BSD / Mac.


Я люблю це рішення. perlє частиною специфікації стандартної бази Linux з 1 травня 2009 року. refspecs.linuxfoundation.org/LSB_4.0.0/LSB-Languages/…
OregonTrail

1
Це легко найкраще рішення для портативності
ecnepsnai

17

Немає можливості його працювати.

Одним із способів є використання тимчасового файлу типу:

TMP_FILE=`mktemp /tmp/config.XXXXXXXXXX`
sed -e "s/abc/def/" some/file > $TMP_FILE
mv $TMP_FILE some/file

Це працює на обох


Чи є причина, що SOME_FILE = "$ (sed -s" s / abc / def / "some / file)"; echo "$ SOME_FILE"> деякий / файл; замість цього не буде працювати
Jason Gross

Схоже, ви обмежені максимальним розміром bash змінної, ні? Не впевнений, що він буде працювати з файлами GB.
аналог

3
Якщо ви створюєте тимчасовий файл, чому б не просто дати розширення для створення резервного файлу (який ви можете потім видалити), як пропонує @kine нижче?
Алекс Дюпюй

13

Відповідь: Ні.

Спочатку прийнята відповідь насправді не робить того, що вимагається (як зазначено в коментарях). (Я знайшов цю відповідь, коли шукав причину, коли file-eв моїх каталогах з'явилося "випадковим чином".)

Мабуть, немає способу sed -iпослідовно працювати як на MacOS, так і на Linuces.

Моя рекомендація, для чого варто, це не оновлюватись на місці sed(у якому є складні режими відмов), а генерувати нові файли та перейменувати їх згодом. Іншими словами: уникайте -i.


9

Цей -iпараметр не є частиною POSIX Sed . Більш портативним методом було б використання Vim в режимі Ex:

ex -sc '%s/alfa/bravo/|x' file
  1. % виберіть усі рядки

  2. s замінити

  3. x зберегти і закрити


Це правильна відповідь. Основна особливість POSIX - це портативність.
Каджукенбо

9

Ось ще одна версія, яка працює на Linux та macOS без використання evalта без необхідності видалення резервних файлів. Він використовує масиви Bash для зберігання sedпараметрів, що чистіше, ніж використання eval:

# Default case for Linux sed, just use "-i"
sedi=(-i)
case "$(uname)" in
  # For macOS, use two parameters
  Darwin*) sedi=(-i "")
esac

# Expand the parameters in the actual call to "sed"
sed "${sedi[@]}" -e 's/foo/bar/' target.file

При цьому не створюється резервний файл, а також файл із доданими лапки.


2
це правильна відповідь. Я б трохи спростив її, але ідея sedi=(-i) && [ "$(uname)" == "Darwin" ] && sedi=(-i '') sed "${sedi[@]}" -e 's/foo/bar/' target.file
Олексій Скрипник

Хороший матеріал! Хоча я віддаю перевагу більш довгій версії для читабельності.
nwinkler

1
Це найкращий спосіб для мене бути сумісним з різною системою.
Віктор Чой

6

Відповідь Стіва Пауелла цілком правильна, звертаючись на сторінку MAN для sed на OSX та Linux (Ubuntu 12.04), підкреслюється несумісність у використанні sed-in 'на місці' для двох операційних систем.

JFYI, не повинно бути місця між -i та будь-якими лапками (які позначають порожнє розширення файлу) з використанням версії Linux sed, таким чином

sed Людина Сторінка Linux

#Linux
sed -i"" 

і

sed OSX Man page

#OSX (notice the space after the '-i' argument)
sed -i "" 

Я обминув це в сценарії, використовуючи команду alias'd і вихідне ім’я ОС ' uname ' в межах bash 'if'. Спроба зберігати рядки команд, що залежать від ОС, у змінних була вражена та пропущена при інтерпретації лапок. Використання " shopt -s expand_aliases " необхідне для розширення / використання псевдонімів, визначених у вашому сценарії. Використання shopt в розглядається тут .


1

Якщо вам потрібно зробити sedмісце в bashсценарії, і ви НЕ хочете, щоб на місці виходили файли .bkp, і у вас є спосіб виявити os (скажімо, за допомогою ostype.sh ), - тоді наступний злом із bashвбудованою оболонкою evalповинен працювати:

OSTYPE="$(bash ostype.sh)"

cat > myfile.txt <<"EOF"
1111
2222
EOF

if [ "$OSTYPE" == "osx" ]; then
  ISED='-i ""'
else # $OSTYPE == linux64
  ISED='-i""'
fi

eval sed $ISED 's/2222/bbbb/g' myfile.txt
ls 
# GNU and OSX: still only myfile.txt there

cat myfile.txt
# GNU and OSX: both print:
# 1111
# bbbb

# NOTE: 
# if you just use `sed $ISED 's/2222/bbbb/g' myfile.txt` without `eval`,
# then you will get a backup file with quotations in the file name, 
# - that is, `myfile.txt""`

0

Можна використовувати губку. Губка - це стара програма Unix, яка міститься в пакеті moreutils (і в ubuntu, і, ймовірно, в debian, і в домашньому мові в mac).

Він буде буферувати весь вміст з труби, дочекатися, коли труба буде закрита (можливо, це означає, що вхідний файл вже закритий), а потім перезаписати:

Із чоловічої сторінки :

Конспект

sed '...' файл | греп '...' | губчастий файл


3
Гарний підхід - на жаль, насправді не допомагає в початковій ситуації, оскільки причиною вдачі до sed було те, що можна використовувати в кросплатформенному сценарії помічників, що використовується на клієнтських машинах, де ви не можете залежати ні від чого іншого, крім системи інструменти, що встановлюються. Хоча це може бути корисним комусь іншому.
dnadlinger

0

У Linux та OS X працює для мене наступне:

sed -i' ' <expr> <file>

наприклад, для файлу, fщо міститьaaabbaaba

sed -i' ' 's/b/c/g' f

приносить aaaccaacaі Linux, і Mac. Зауважте, що існує цитується рядок, що містить пробіл , без пробілу між -iрядком та рядком. Одиничні або подвійні лапки, як робота.

У Linux я використовую bashверсію 4.3.11 під Ubuntu 14.04.4 та на Mac версії 3.2.57 під ОС X 10.11.4 El Capitan (Darwin 15.4.0).


1
Нічого собі, я радий, що я не слухав усіх, хто говорив вище, і говорив, що це неможливо, і продовжував читати! Це справді просто працює. Дякую!
Особа

4
Не працює для мене на Mac OS ... створюється новий файл з пробілом, доданим до кінця імені файлу
Frédéric

Не працює для мене і на Mac (/ usr / bin / sed) - тепер у мене є додатковий файл резервної копії з пробілом, доданим до імені файлу :-(
paul_h

Спробуйте взяти простір з єдиних цитат, це працює для мене.
дпс

0

Я зіткнувся з цією проблемою. Єдиним швидким рішенням було замінити sed в mac на версію gnu:

brew install gnu-sed

Це дублює існуючу відповідь з набагато більш докладно з 2015.
tripleee
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.