Как заменить всю строку в текстовом файле на номер строки

152

У меня есть ситуация, когда я хочу, чтобы bash-скрипт заменял всю строку в файле. Номер строки всегда одинаков, поэтому это может быть жестко заданная переменная.

Я не пытаюсь заменить некоторую подстроку в этой строке, я просто хочу полностью заменить эту строку новой строкой.

Существуют ли для этого методы bash (или что-то простое, что можно добавить в сценарий .sh).

user788171
источник

Ответы:

228

Не самое большое, но это должно работать:

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

где Nдолжно быть заменено номером вашей целевой строки. Это заменяет строку в исходном файле. Чтобы сохранить измененный текст в другом файле, опустите -iопцию:

sed 'Ns/.*/replacement-line/' file.txt > new_file.txt
chepner
источник
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....
Chepner
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
chepner
24
Может быть, бесполезно для интерпретатора / компилятора, но не для человека, читающего его. Код @ Кайла хорошо читается слева направо; идиома, которую вы использовали в IMO, отсутствует из-за того, что глагол находится перед существительным.
Клейтон Стэнли
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 .

РЕДАКТИРОВАТЬ: более короткая и элегантная программа AWK от @chepner в комментариях ниже:

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

Только для записи (т. Е. Строки) номер 4, замените всю запись на строку «разные». Затем для каждой входной записи распечатайте запись.

Ясно, что мои навыки AWK ржавые! Спасибо, @chepner.

РЕДАКТИРОВАТЬ: и увидеть также еще более короткую версию от @Dennis Williamson:

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

Как это работает, объясняется в комментариях: 1всегда оценивается как true, поэтому всегда выполняется связанный блок кода. Но нет никакого связанного блока кода, что означает, что AWK выполняет свое действие по умолчанию просто печати всей строки. AWK предназначен для кратких программ, подобных этой.

steveha
источник
3
Немного короче awk 'NR==4 {$0="different"} { print }' input_file.txt.
chepner
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, который не выводит файл на стандартный вывод: stackoverflow.com/a/13438118/4669135
Габриэль Девиллерс
Это правильный ответ, но почему уродливый разрыв строки? sed '1 cnewline text' test.txtсделал бы это также.
dev-null
7

в bash замените 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

РЕДАКТИРОВАТЬ

На самом деле в этом решении есть некоторые проблемы, с символами "\ 0", "\ t" и "\"

"\ t", может быть решено путем помещения IFS = перед прочтением: "\", в конце строки с -r

IFS= read -r line

но для "\ 0" переменная усекается, в чистом bash нет решения: присвоить строку, содержащую нулевой символ (\ 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

Отличный ответ от Чепнера. Это работает для меня в Bash Shell.

 # 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 

orignial 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
AndreH
источник
1
Зачем вам нужен цикл для этого примера, просто запутывает решение? line=5; sed -i "${line}s/.*/replaced by '$i'!/" output.dat
Роб
Вам не нужна петля для этого
chandu vutukuri