Jak zamienić cały wiersz w pliku tekstowym na numer wiersza


152

Mam sytuację, w której chcę, aby skrypt bash zastąpił całą linię w pliku. Numer wiersza jest zawsze taki sam, więc może to być zmienna zakodowana na stałe.

Nie próbuję zamienić jakiegoś podciągu w tym wierszu, po prostu chcę całkowicie zastąpić ten wiersz nowym wierszem.

Czy są do tego jakieś metody basha (lub coś prostego, które można wrzucić do skryptu .sh).

Odpowiedzi:


228

Nie najlepszy, ale to powinno działać:

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

gdzie Nnależy zastąpić docelowym numerem linii. Zastępuje wiersz w oryginalnym pliku. Aby zapisać zmieniony tekst w innym pliku, upuść -iopcję:

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

1
Czy istnieje sposób, aby sed zmodyfikował dany plik zamiast zrzucać go na ekran?
user788171

8
Dla mnie jest napisane: sed: -e expression #1, char 26: unknown option to ``s'a moja linia jest: 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. Dowolny pomysł?
Danijel

4
Każdy /w tekście zastępczym byłby interpretowany jako zamykający ukośnik spolecenia, chyba że jest to znak ucieczki ( \/). Najłatwiej jednak jest wybrać inny, nieużywany znak jako separator. Na przykład sed -i '7s{.*}{<param-value>http://...}' $TCE_SVN_HOME/trunk....
chepner

4
To wyjaśnienie jest trochę zagmatwane i nie wydaje się działać. Jeśli macie problemy z sedem i jego możliwościami ustawiania separatora, możecie rzucić okiem na to: grymoire.com/Unix/Sed.html#uh-2 Mówi, że pierwszy znak za znakiem sokreśla separator. Tak więc, aby zmienić polecenie, aby użyć na przykład polecenia #, wyglądałoby to tak:sed -i '7s#.*#<param-value>http://localhost:8080/ASDF/services/REWS.REWSHttpSoap12Endpo‌​int/</param-value>#'
func0der

7
Aby rozwinąć @ meonlol, macOS wymaga również podania -eif -i; tak więc poprawne polecenie to:sed -e 'Ns/.*/replacement-line/' -i '' file.txt
ELLIOTTCABLE

44

Właściwie użyłem tego skryptu do zastąpienia linii kodu w pliku cron na serwerach UNIX naszej firmy. Wykonaliśmy to jako normalny skrypt powłoki i nie mieliśmy żadnych problemów:

#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

Nie chodzi o numer linii, ale możesz łatwo przełączyć się na system oparty na numerach linii, umieszczając numer linii przed s/znakiem i zastępując go symbolem wieloznacznym the_original_line.


17
Bezużyteczne wykorzystanie kota:sed -e "s/.../.../" /dir/file > /dir/temp_file
chepner

24
Może bezużyteczne dla interpretera / kompilatora, ale nie dla człowieka czytającego. Kod @ Kyle'a ładnie czyta się od lewej do prawej; idiom, którego użyłeś IMO, nie jest, ze względu na fakt, że czasownik występuje przed rzeczownikiem.
Clayton Stanley

1
Czy jest sposób, aby połączyć te dwie linie, na przykład przez coś takiegosed -e "s/.../.../" /dir/file > /dir/file
Pubudu Dodangoda

22

Załóżmy, że chcesz zastąpić wiersz 4 tekstem „inny”. Możesz używać AWK w następujący sposób:

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

AWK traktuje dane wejściowe jako „rekordy” podzielone na „pola”. Domyślnie jeden wiersz to jeden rekord. NRto liczba wyświetlonych rekordów. $0reprezentuje bieżący pełny rekord (gdzie $1jest pierwszym polem z rekordu i tak dalej; domyślnie pola są słowami z wiersza).

Tak więc, jeśli bieżący numer wiersza to 4, wypisuje łańcuch „inny”, ale w przeciwnym razie wypisuje wiersz bez zmian.

W AWK kod programu zawarty w jest { }uruchamiany raz na każdym rekordzie wejściowym.

Musisz cytować program AWK w pojedynczych cudzysłowach, aby powłoka nie próbowała interpretować rzeczy takich jak $0.

EDYCJA: Krótszy i bardziej elegancki program AWK od @chepner w komentarzach poniżej:

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

Tylko dla rekordu (tj. Linii) numer 4, zastąp cały rekord ciągiem „różne”. Następnie wydrukuj rekord dla każdego rekordu wejściowego.

Najwyraźniej moje umiejętności AWK są zardzewiałe! Dziękuję @chepner.

EDYCJA: i zobacz także jeszcze krótszą wersję od @Dennis Williamson:

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

Jak to działa, wyjaśniono w komentarzach: 1zawsze zwraca wartość true, więc powiązany blok kodu zawsze działa. Ale nie ma powiązanego bloku kodu, co oznacza, że ​​AWK wykonuje swoją domyślną akcję, po prostu wypisuje całą linię. AWK został zaprojektowany, aby zezwalać na takie zwięzłe programy.


3
Nieco krótsza: awk 'NR==4 {$0="different"} { print }' input_file.txt.
chepner

2
@chepner: Trochę krócej:awk 'NR==4 {$0="different"}1' input_file.txt
Wstrzymano do odwołania.

@DennisWilliamson, nawet nie wiem, jak to działa! Co robi to zakończenie 1? (Uwaga: przetestowałem to i rzeczywiście działa! Po prostu nie rozumiem jak.)
steveha

1
Pomyślałem, że będzie sposób na skrócenie bezwarunkowego druku! @stevaha: 1 to po prostu prawdziwa wartość, co oznacza „wzorzec”, który zawsze pasuje. Domyślną akcją dla dopasowania jest wydrukowanie bieżącego wiersza.
chepner

19

Biorąc pod uwagę ten plik testowy (test.txt)

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

poniższe polecenie zamieni pierwszą linię na „tekst nowego wiersza”

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

Wynik:

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

więcej informacji można znaleźć tutaj

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


2
To powinna być prawidłowa odpowiedź. Flaga c robi dokładnie to, co jest wymagane (zastępuje numerowaną linię podanym tekstem).
adelphus

Alternatywna składnia z sedem, która nie odzwierciedla pliku na stdout: stackoverflow.com/a/13438118/4669135
Gabriel Devillers

To właściwa odpowiedź, ale dlaczego brzydka przerwa w linii? sed '1 cnewline text' test.txtzrobiłby to również.
dev-null

7

w bash zamień N, M na numery linii, a xxx yyy na to, co chcesz

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

EDYTOWAĆ

W rzeczywistości w tym rozwiązaniu występują problemy ze znakami „\ 0” „\ t” i „\”

"\ t", można rozwiązać, umieszczając IFS = przed przeczytaniem: "\" na końcu linii z -r

IFS= read -r line

ale dla "\ 0" zmienna jest obcięta, nie ma rozwiązania w czystym bash: Przypisz ciąg zawierający znak null (\ 0) do zmiennej w Bash Ale w normalnym pliku tekstowym nie ma znaku nul \ 0

Perl byłby lepszym wyborem

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

Nawet nie pomyślałem o wypróbowaniu czystego rozwiązania 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

Doskonała odpowiedź od Chepnera. Działa dla mnie w 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`

tutaj
index- Line no
newLine- nowy ciąg linii, który chcemy zamienić.


Podobnie poniższy kod służy do odczytu konkretnej linii w pliku. Nie wpłynie to na rzeczywisty plik.

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

tutaj
!d- usunie linie inne niż linia nr. $index Otrzymamy wynik jako ciąg linii o numerze nie $indexw pliku.


ale to mój bash, dlaczego wynik pokazuje się tylko ${newline}?
sikisis

1

Na komputerze Mac, którego użyłem

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

-2

Możesz nawet przekazać parametry do polecenia 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

Wykonanie ./test.sh daje nowy output.dat

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

1
dlaczego potrzebujesz pętli w tym przykładzie, po prostu zaciemniasz rozwiązanie? line=5; sed -i "${line}s/.*/replaced by '$i'!/" output.dat
Rob

Nie potrzebujesz do tego pętli
chandu vutukuri
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.