Як замінити цілий рядок у текстовому файлі на номер рядка


152

У мене ситуація, коли я хочу, щоб сценарій bash замінив цілий рядок у файлі. Номер рядка завжди однаковий, так що може бути твердо кодована змінна.

Я не намагаюся замінити деяку підрядку в цьому рядку, я просто хочу повністю замінити її на новий рядок.

Чи є якісь методи bash для цього (або щось просте, що може бути кинуто у .sh-скрипт).

Відповіді:


228

Не найбільше, але це має працювати:

sed -i 'Ns/.*/replacement-line/' file.txt

де Nслід замінити номер цільової лінії Це замінює рядок у вихідному файлі. Щоб зберегти змінений текст у іншому файлі, відмініть -iопцію:

sed 'Ns/.*/replacement-line/' file.txt > new_file.txt

1
Чи є спосіб змусити sed змінити відповідний файл, а не скидати на екран?
user788171

8
Для мене це говорить: sed: -e expression #1, char 26: unknown option to ``s'і моя лінія: sed -i '7s/.*/<param-value>http://localhost:8080/ASDF/services/REWS.REWSHttpSoap12Endpoint/</param-value>/' $TCE_SVN_HOME\trunk\tce\EWC\WebContent\WEB-INF\web.xml. Будь-яка ідея?
Даніель

4
Кожен /у тексті заміни інтерпретується як косою косою чергою sкоманди, якщо не уникнути ( \/). Найпростіше зробити, однак, вибрати інший, невикористаний символ як роздільник. Наприклад, sed -i '7s{.*}{<param-value>http://...}' $TCE_SVN_HOME/trunk....
чепнер

4
Це пояснення трохи заплутане і, здається, не працює. Якщо у вас є проблеми з sed і його можливостями налаштування роздільника, ви можете ознайомитись з цим: grymoire.com/Unix/Sed.html#uh-2 У ньому йдеться про те, що перший символ, що sвизначається, визначає роздільник. Отже, щоб змінити вашу команду, щоб використовувати, наприклад, #було б так:sed -i '7s#.*#<param-value>http://localhost:8080/ASDF/services/REWS.REWSHttpSoap12Endpo‌​int/</param-value>#'
func0der

7
Для розширення на @ meonlol, macOS вимагає також -eif, якщо -iтакож вказано; Таким чином, правильна команда стає:sed -e 'Ns/.*/replacement-line/' -i '' file.txt
ELLIOTTCABLE

44

Я фактично використовував цей скрипт для заміни рядка коду у файлі cron на серверах UNIX нашої компанії деякий час. Ми виконали його як звичайний скрипт оболонки і не мали проблем:

#Create temporary file with new line in place
cat /dir/file | sed -e "s/the_original_line/the_new_line/" > /dir/temp_file
#Copy the new file over the original file
mv /dir/temp_file /dir/file

Це не йде за номером рядка, але ви можете легко перейти до системи на основі номера рядка, поставивши номер рядка перед s/і поставивши замість підстановки the_original_line.


17
Марне використання кота:sed -e "s/.../.../" /dir/file > /dir/temp_file
чепнер

24
Можливо, для перекладача / укладача це марно, але не для того, щоб його читати людина. @ Код Кайла добре читається зліва направо; ідіома, якою ви користувались ІМО, ні, через те, що дієслово стоїть перед іменником.
Клейтон Стенлі

1
Чи є спосіб об'єднати ці два рядки, наприклад, на щось подібне,sed -e "s/.../.../" /dir/file > /dir/file
Pubudu Dodangoda

22

Припустимо, ви хочете замінити рядок 4 текстом "інший". Ви можете використовувати AWK так:

awk '{ if (NR == 4) print "different"; else print $0}' input_file.txt > output_file.txt

AWK вважає вхід "записами", поділеними на "поля". За замовчуванням один рядок - це один запис. NR- кількість переглянутих записів. $0представляє поточний повний запис (в той час як $1це перше поле з запису тощо), за замовчуванням поля - це слова з рядка).

Отже, якщо номер поточного рядка дорівнює 4, надрукуйте рядок "інше", але в іншому випадку надрукуйте рядок без змін.

У AWK програмний код, що додається, { }працює один раз на кожному вхідному записі.

Потрібно навести програму AWK в одиничних лапках, щоб оболонка не намагалася інтерпретувати такі речі, як $0.

EDIT: Коротка та більш елегантна програма AWK від @chepner в коментарях нижче:

awk 'NR==4 {$0="different"} { print }' input_file.txt

Тільки для запису (тобто рядка) № 4 замініть весь запис рядком "інше". Потім для кожного вхідного запису надрукуйте запис.

Ясно мої навички AWK іржаві! Дякую, @chepner.

EDIT: і див. Ще більш коротку версію від @Dennis Williamson:

awk 'NR==4 {$0="different"} 1' input_file.txt

Як це працює, пояснюється в коментарях: 1завжди оцінює істину, тому пов'язаний блок коду завжди працює. Але не існує асоційованого кодового блоку, а це означає, що AWK виконує за замовчуванням дію просто надрукувати весь рядок. AWK розроблений, щоб дозволити подібні програми.


3
Трохи коротше: awk 'NR==4 {$0="different"} { print }' input_file.txt.
чепнер

2
@chepner: Трохи коротше:awk 'NR==4 {$0="different"}1' input_file.txt
Призупинено до подальшого повідомлення.

@DennisWilliamson, я навіть не знаю, як це працює! Що робить цей трейлінг 1? (Примітка. Я перевірив це, і він справді працює! Я просто не розумію, як.)
steveha

1
Я подумав, що буде спосіб скоротити безумовний друк! @stevaha: 1 - це просто справжнє значення, що означає "візерунок", який завжди відповідає. І типовою дією для відповідності є друк поточного рядка.
чепнер

19

Враховуючи цей тестовий файл (test.txt)

Lorem ipsum dolor sit amet,
consectetur adipiscing elit. 
Duis eu diam non tortor laoreet 
bibendum vitae et tellus.

наступна команда замінить перший рядок на "текст нового рядка"

$ sed '1 c\
> newline text' test.txt

Результат:

newline text
consectetur adipiscing elit. 
Duis eu diam non tortor laoreet 
bibendum vitae et tellus.

більше інформації можна знайти тут

http://www.thegeekstuff.com/2009/11/unix-sed-tutorial-append-insert-replace-and-count-file-lines/


2
Це має бути правильна відповідь. Прапор c робить саме те, що потрібно (замінює нумерований рядок даним текстом).
adelphus

Альтернативний синтаксис з sed, який не відкликає файл у stdout: stackoverflow.com/a/13438118/4669135
Габріель Девіллерс

Це правильна відповідь, але чому потворна лінія порушується? sed '1 cnewline text' test.txtзробив би це також.
dev-null

7

в баші замініть N, M на номери рядків і xxx yyy на те, що ви хочете

i=1
while read line;do
  if((i==N));then
    echo 'xxx'
  elif((i==M));then
    echo 'yyy'
  else
    echo "$line"
  fi
  ((i++))
done  < orig-file > new-file

EDIT

Насправді в цьому рішенні є деякі проблеми, з символами "\ 0" "\ t" і "\"

"\ t", можна вирішити, поставивши IFS = перед читанням: "\", в кінці рядка з -r

IFS= read -r line

але для "\ 0" змінна урізана, немає рішення в чистому баші: призначте рядок, що містить нульовий символ (\ 0), змінної в Bash Але у звичайному текстовому файлі немає нульового символу \ 0

perl був би кращим вибором

perl -ne 'if($.==N){print"xxx\n"}elsif($.==M){print"yyy\n"}else{print}' < orig-file > new-file

Я навіть не думав пробувати чисте рішення BASH!
steveha

4
# Replace the line of the given line number with the given replacement in the given file.
function replace-line-in-file() {
    local file="$1"
    local line_num="$2"
    local replacement="$3"

    # Escape backslash, forward slash and ampersand for use as a sed replacement.
    replacement_escaped=$( echo "$replacement" | sed -e 's/[\/&]/\\&/g' )

    sed -i "${line_num}s/.*/$replacement_escaped/" "$file"
}

3

Відмінна відповідь від Чепнера. Це працює для мене в баш-шеллі.

 # To update/replace the new line string value with the exiting line of the file
 MyFile=/tmp/ps_checkdb.flag

 `sed -i "${index}s/.*/${newLine}/" $MyFile`

тут
index- Ні рядка
newLine- новий рядок, який ми хочемо замінити.


Аналогічно нижче код використовується для читання певного рядка у файлі. Це не вплине на фактичний файл.

LineString=`sed "$index!d" $MyFile` 

тут
!d- буде видалено рядки, відмінні від рядка no. $index Отже, ми отримаємо вихід у вигляді рядка рядка no $indexу файлі.


але це мій баш, чому результат лише показує ${newline}?
sikisis

1

На mac я використовував

sed -i '' -e 's/text-on-line-to-be-changed.*/text-to-replace-the=whole-line/' file-name

-2

Ви навіть можете передавати параметри команді sed:

test.sh

#!/bin/bash
echo "-> start"
for i in $(seq 5); do
  # passing parameters to sed
  j=$(($i+3))
  sed -i "${j}s/.*/replaced by '$i'!/" output.dat
done
echo "-> finished"
exit 

оригінальний output.dat:

a
b
c
d
e
f
g
h
i
j

Виконання ./test.sh дає новий output.dat

a
b
c
replaced by '1'!
replaced by '2'!
replaced by '3'!
replaced by '4'!
replaced by '5'!
i
j

1
навіщо вам потрібен цикл для цього прикладу, просто обмацує рішення? line=5; sed -i "${line}s/.*/replaced by '$i'!/" output.dat
Роб

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