Як я можу використовувати змінні в LHS та RHS заміщення sed?


60

Я хочу зробити:

cat update_via_sed.sh | sed 's/old_name/new_name/' > new_update_via_sed.sh

в моїй програмі.

Але я хочу використовувати змінні, наприклад

old_run='old_name_952'
new_run='old_name_953'

Я намагався їх використовувати, але підміна не відбувається (помилки немає). Я намагався:

cat update_via_sed.sh | sed 's/old_run/new_run/'
cat update_via_sed.sh | sed 's/$old_run/$new_run/'
cat update_via_sed.sh | sed 's/${old_run}/${new_run}/'

2
Відповідь можна знайти у розділі Використовувати параметр у аргументі команди .
манатура

Відповіді:


44

Ви можете зробити:

sed "s/$old_run/$new_run/" < infile > outfile

Але будьте обережні, що це $old_runбуло б прийнято як регулярний вираз, і тому будь-які спеціальні символи, що містять змінну, такі як, /або .повинні були б уникнути . Точно так же, в $new_run, то &і \символи повинні бути оброблені спеціально , і ви б рятуючись від /і символів нового рядка в ньому.

Якщо вміст $old_runабо $new_runне знаходиться під вашим контролем, важливо виконати це сканування, або в іншому випадку цей код становить вразливість введення коду .


3
Я використовував одинарну цитату в sed params, перейшов на подвійні лапки, і це спрацювало файл. Дивовижно.
Аакаш

це не те, що sedпоодинці не використовуватиме регулярний вираз, але sed -Eбуде?
Ківі

@Kiwy, ні. -eполягає в тому, щоб вказати sed e xpression , щоб ви могли передавати більше (як in sed -e exp1 -e exp2) або комбінації файлів та виразів ( sed -f file -e exp). Ви можете заплутатися в тому, -Eщо з деякими реалізаціями вказує sed використовувати ERE замість BRE.
Стефан Шазелас

Я помилково вписав, але добре, що пояснюють, чому у мене так багато проблем із використанням sed, без -Eя лише освоюю розширений регулярний вираз, а не звичайний. Дякую за пояснення.
Ківі

@Kiwy, див. Також пов'язані запитання і відповіді, щоб отримати додаткові параметри, підтримувані деякими sedреалізаціями для ще більш регулярних синтаксисів виразів.
Стефан Шазелас

31

Це спрацювало:

cat update_via_sed.sh | sed 's/'"$old_run"'/'"$new_run"'/'

Оскільки я хочу «повторно використовувати» той самий файл, я фактично використовую це для всіх, хто бажає подібного підходу:

cat update_via_sed.sh | sed 's/'"$old_run"'/'"$new_run"'/' > new_update; mv -f new_update update_via_sed.sh

Вище створений новий файл потім видаляє поточний файл, ніж перейменувати новий файл на поточний файл.


4
Вам не потрібні кішки, телевізори чи додаткові '', якщо у вас не буде спеціальних символів. Так це sed -i s/"$old"/"$new"/ update_via_sed.sh. Однак майте на увазі, що це не працює для будь-якого значення старого та нового. наприклад, він не повинен містити / etc., оскільки $ old - це все ще пошук, а $ new - шаблон заміни, коли деякі символи мають особливі значення.
frostschutz

1
sed -i "s/$old/$new/"також добре (з вищезазначеними запобіжними засобами). sedзазвичай вважає роздільник першим символом після sкоманди - тобто, якщо ваші змінні містять косої риски, /але не дозволяють сказати в ( @), ви можете використовувати "s @ $ old @ $ new @".
петерф

22

Ви можете використовувати змінні в sed до тих пір, поки це буде в подвійній цитаті (не в одній цитаті):

sed "s/$var/r_str/g" file_name >new_file

Якщо ви маєте просунуту косу рису ( /) у змінній, тоді використовуйте інший роздільник, як показано нижче

sed "s|$var|r_str|g" file_name >new_file

1
+1 для згадки про необхідність використання подвійних лапок. Це була моя проблема.
крапчастий

1
Саме те, що я шукав, включаючи використання, |як і в моєму випадку, змінні містять шлях, таким чином він є /в ньому
yorch

Чомусь труба |працювала для мене на MacOS 10.14, але іншим сепараторам подобається @чи #ні. У мене також були косої риски в моєму оточенні.
davidenke

21

"in-place" sed (usng the -i прапор) була відповіддю. Завдяки петерфі.

sed -i "s@$old@$new@" file

2
Ви маєте цитати в неправильному місці. лапки повинні бути навколо змінних, а не s@(що жодним чином не особливо для оболонки). Найкраще все помістити всередину цитат, як sed -i "s@$old@$new@" file. Зверніть увагу, що -iце не стандартний sedваріант.
Стефан Шазелас

як я можу уникнути $цієї команди. Мовляв, я збираюся $xxxв цілому змінити значення змінної $JAVA_HOME. Я спробував sed -i "s@\$xxx@$JAVA_HOME@" file, але помилка говоритьno previous regular expression
хакунамі

3

man bash дає це про єдине цитування

Замикання символів в одинарних лапках зберігає буквальне значення кожного символу в лапках. Одиночна цитата може не виникати між окремими цитатами, навіть якщо передує зворотна косою рисою.

Що б ви не ввели в командному рядку, bash інтерпретує його, а потім надсилає результат програмі, якій він повинен бути надісланий. У цьому випадку, якщо ви використовуєте sed 's/$old_run/$new_run/', bash спочатку бачить sed, він розпізнає його як виконуваний файл, присутній у $PATHзмінній . sedВиконуваний файл вимагає введення. Bash шукає вхід і знаходить 's/$old_run/$new_run/'. Поодинокі цитати говорять, що баш не інтерпретувати зміст у них і передавати їх такими, якими вони є. Отже, bash потім передає їх на sed. Sed видає помилку, оскільки $може статися лише в кінці рядка.

Замість цього, якщо ми використовуємо подвійні лапки, тобто, "s/$old_run/$new_run/"bash бачить це і інтерпретує $old_runяк ім'я змінної та робить підміну (ця фаза називається розширенням змінної). Це дійсно те, чого ми вимагали.

Але, ви повинні бути обережними, використовуючи подвійні лапки, тому що вони інтерпретуються спочатку bash, а потім надаються sed. Отже, перед використанням їх потрібно уникати деяких символів типу `.


1

Ви ризикуєте стати легковажною - чи вважали ви perl.

Що, крім іншого, - дуже добре виконує операції "у стилі sed", але також є більш повною функцією програмування.

Це виглядає , як у вашому прикладі, що ви на самому справі намагаєтеся зробити , це збільшення числа.

То як щодо:

#!/usr/bin/env perl
use strict;
use warnings 'all'; 

while ( <DATA> ) {
    s/old_name_(\d+)/"old_name_".($1+1)/e;
    print;
}

__DATA__
old_name_932

або як єдиний лайнер - стиль sed:

perl -pe 's/old_name_(\d+)/"old_name_".($1+1)/e'

1
$ echo $d1 | sed -i 's/zkserver1/'${d1}'/g' deva_file
sed: -e expression #1, char 26: unterminated `s' command 

В $d1я зберігаю цінності

Я не можу замінити значення зі змінної, тому я спробував наступну команду, яка працює

echo $d1 | sed -i 's/zkserver1/'"${d1}"'/g' deva_file

пропущена подвійна лапка "${d1}", це була помилка


-2

Передбачається $old_runі $new_runвже встановлено:

cat update_via_sed.sh | eval $(print "sed 's/$old_run/$new_run/g'")

Це відображає зміни на екрані. Ви можете перенаправити вихідний файл у разі потреби.


-2

Для використання змінної в sed використовуйте подвійні лапки "", щоб укласти змінну у шаблоні, який ви використовуєте. Наприклад: a = "Привіт" Якщо ви хочете додати змінну 'a' до шаблону, ви можете використовувати нижче: sed -n "1, / pattern" $ {a} "pattern / p" назва файлу


2
Розширення $aтут не цитується, це означає, що будь-які пробіли у його значенні викликатимуть розділення слова в оболонці.
Kusalananda

-2

Ви також можете використовувати Perl:

perl -pi -e ""s/$val1/$val2/g"" file

Розглянемо змінні, які можуть мати пробіли у своїх значеннях. Порожні рядки навколо виразу Perl ( "") нічого не дадуть.
Кусалаланда

-2

розірвати рядок

bad => linux бачити рядок $ old_run:

sed 's/$old_run/$new_run/'

good => linux див. змінну $ old_run:

sed 's/'$old_run'/'$new_run'/'

1
А якщо $old_runабо $new_runмістить пробіли?
Кусалаланда
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.