Znajdowanie i zastępowanie wewnątrz pliku tekstowego za pomocą polecenia Bash

Jaki jest najprostszy sposób, aby znaleźć i zastąpić dany łańcuch wejściowy, powiedzmy abc, i zastąpić innym łańcuchem, powiedzmy XYZ w pliku /tmp/file.txt?

Piszę aplikację i używam Ironpythona do wykonywania poleceń przez SSH-ale nie znam tak dobrze Uniksa i nie wiem czego szukać.

Słyszałem, że Bash, oprócz tego, że jest interfejsem wiersza poleceń, może być bardzo potężnym językiem skryptowym. Więc jeśli to prawda, zakładam, że możesz wykonywać takie czynności jak to.

Czy Mogę to zrobić za pomocą Basha i jaki jest najprostszy (jednolinijkowy) skrypt, aby osiągnąć mój cel?

Author: Ash, 2009-02-08

15 answers

Najprostszym sposobem jest użycie sed (lub Perla):

sed -i -e 's/abc/XYZ/g' /tmp/file.txt

, która wywoła sed do wykonania edycji w miejscu ze względu na opcję -i. Można to nazwać z bash.

Jeśli naprawdę chcesz używać just bash, to może działać:

while read a ; do echo ${a//abc/XYZ} ; done < /tmp/file.txt > /tmp/file.txt.t ; mv /tmp/file.txt{.t,}

Jest to pętla nad każdą linią, wykonująca podstawianie i zapisująca do pliku tymczasowego(nie chcemy blokować wejścia). Ruch na końcu przenosi się tymczasowo do pierwotnej nazwy.

 672
Author: johnny,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-26 16:27:54

Manipulacja Plikami nie jest zwykle wykonywana przez Basha, ale przez programy wywoływane przez Basha, np.:

> perl -pi -e 's/abc/XYZ/g' /tmp/file.txt

Flaga -i mówi jej, aby wykonała zastąpienie w miejscu.

Zobacz man perlrun aby uzyskać więcej informacji, w tym jak wykonać kopię zapasową oryginalnego pliku.

 148
Author: Alnitak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-05 12:01:48

Byłam zaskoczona, bo natknęłam się na to...

Istnieje polecenie "replace" , które wysyła z pakietem "mysql-server" , więc jeśli go zainstalowałeś wypróbuj:

# replace string abc to XYZ in files
replace "abc" "XYZ" -- file.txt file2.txt file3.txt

# or pipe an echo to replace
echo "abcdef" |replace "abc" "XYZ"

Zobacz man replace aby dowiedzieć się więcej na ten temat...

 59
Author: rayro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-09 15:35:27

Bash, podobnie jak inne powłoki, jest tylko narzędziem do koordynowania innych poleceń. Zazwyczaj próbowałbyś używać standardowych komend Uniksa, ale oczywiście możesz używać Bash do wywoływania czegokolwiek, w tym własnych skompilowanych programów, innych skryptów powłoki, skryptów Pythona i Perla itp.

W tym przypadku można to zrobić na kilka sposobów.

Jeśli chcesz odczytać plik i zapisać go do innego pliku, wykonując wyszukiwanie / zamianę, użyj sed:

sed 's/abc/XYZ/g' <infile >outfile

Jeśli chcesz edytować plik w umieść (tak jakby otwierał plik w edytorze, edytował go, a następnie zapisywał) instrukcje dostarczania do edytora linii ' ex '

echo "%s/abc/XYZ/g
w
q
" | ex file

Ex jest jak vi bez trybu pełnoekranowego. Możesz nadać mu te same polecenia, co w wierszu ':' vi.

 37
Author: slim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-08 12:35:54

Znalazłem ten wątek m.in. i zgadzam się, że zawiera najbardziej kompletne odpowiedzi więc ja też dodaję swoje:

1) sed i ed są tak useful...by ręka!!! Spójrz na ten kod od @ Johnny:

sed -i -e 's/abc/XYZ/g' /tmp/file.txt

2) gdy moim ograniczeniem jest użycie go przez skrypt powłoki, wtedy żadna zmienna nie może być użyta wewnątrz zamiast abc lub XYZ! to wydaje się zgadzać z tym, co przynajmniej Rozumiem. Więc nie mogę używać:

x='abc'
y='XYZ'
sed -i -e 's/$x/$y/g' /tmp/file.txt
#or,
sed -i -e "s/$x/$y/g" /tmp/file.txt
Ale co możemy zrobić? Jak, @ Johnny powiedział użyj a ' while read..."ale, niestety to nie koniec historii. Ze mną dobrze działało:
#edit user's virtual domain
result=
#if nullglob is set then, unset it temporarily
is_nullglob=$( shopt -s | egrep -i '*nullglob' )
if [[ is_nullglob ]]; then
   shopt -u nullglob
fi
while IFS= read -r line; do
   line="${line//'<servername>'/$server}"
   line="${line//'<serveralias>'/$alias}"
   line="${line//'<user>'/$user}"
   line="${line//'<group>'/$group}"
   result="$result""$line"'\n'
done < $tmp
echo -e $result > $tmp
#if nullglob was set then, re-enable it
if [[ is_nullglob ]]; then
   shopt -s nullglob
fi
#move user's virtual domain to Apache 2 domain directory
......

3) Jak widać, jeśli nullglob jest ustawiony wtedy, zachowuje się dziwnie, gdy istnieje łańcuch zawierający * jak w

<VirtualHost *:80>
 ServerName www.example.com

Który staje się

<VirtualHost ServerName www.example.com
Nie ma wspornika kąta zakończenia i Apache2 nie może nawet załadować!

4) ten rodzaj parsowania powinien być wolniejszy niż wyszukiwanie jednokrotne i zamiana, ale jak już widziałeś, istnieją 4 zmienne dla 4 różnych wzorców wyszukiwania z jednego tylko cyklu parsowania!

Najbardziej odpowiednie rozwiązanie, jakie mogę wymyślić przy podanych założeniach problemu.

 32
Author: centurian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-07 14:48:02

To jest stary post, ale dla każdego, kto chce używać zmiennych, ponieważ @ centurian powiedział, że pojedyncze cudzysłowy nic nie znaczą, nie zostaną rozszerzone.

Prostym sposobem na uzyskanie zmiennych jest łączenie łańcuchów znaków, ponieważ odbywa się to przez Zestawienie w bash, powinno działać:

sed -i -e 's/'"$var1"'/'"$var2"'/g' /tmp/file.txt

 30
Author: zcourts,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-07 12:48:56

Możesz użyć sed

sed -i 's/abc/XYZ/gi' /tmp/file.txt

Użyj i do ignorowania przypadku, jeśli nie jesteś pewien, że tekst do znalezienia to abc lub ABC lub AbC ,...

Możesz użyć find i sed, jeśli nie masz teraz swojej nazwy pliku:

 find ./ -type f -exec sed -i 's/abc/XYZ/gi' {} \;

Znajdź i zamień we wszystkich plikach Pythona:

find ./ -iname "*.py" -type f -exec sed -i 's/abc/XYZ/gi' {} \;
 12
Author: MmParvin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-14 09:45:22

Możesz również użyć polecenia ed do wyszukiwania w pliku i zastąpienia:

# delete all lines matching foobar 
ed -s test.txt <<< $'g/foobar/d\nw' 

Zobacz więcej na stronie bash-hackers

 11
Author: olegtaranenko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-13 17:25:54

Bądź ostrożny, jeśli zastąpisz adresy URL znakiem"/".

Przykład jak to zrobić:

sed -i "s%http://domain.com%http://www.domain.com/folder/%g" "test.txt"

Pobrano z: http://www.sysadmit.com/2015/07/linux-reemplazar-texto-en-archivos-con-sed.html

 6
Author: Linux4you,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-27 19:08:59

Jeśli plik, nad którym pracujesz nie jest tak duży, a tymczasowe przechowywanie go w zmiennej nie stanowi problemu, możesz użyć Bash string substitution na całym Pliku na raz - nie ma potrzeby przechodzić przez niego linia po linii:

file_contents=$(</tmp/file.txt)
echo "${file_contents//abc/XYZ}" > /tmp/file.txt

Cała zawartość pliku będzie traktowana jako jeden długi łańcuch znaków, łącznie z linebreaks.

XYZ może być zmienną np $replacement, A zaletą nieużywania sed jest to, że nie musisz obawiać się, że łańcuch wyszukiwania lub zamiany może zawierać sed znak ogranicznika wzorca (zazwyczaj, ale niekoniecznie,/). Wadą jest brak możliwości używania wyrażeń regularnych lub jakichkolwiek bardziej wyrafinowanych operacji sed.

 4
Author: johnraff,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-15 02:09:16

Aby edytować tekst w pliku nieinteraktywnie, potrzebujesz wbudowanego edytora tekstu, takiego jak vim.

Oto prosty przykład użycia go z linii poleceń:

vim -esnc '%s/foo/bar/g|:wq' file.txt

Jest to odpowiednik@slim answer z ex edytora, który jest w zasadzie tym samym.

Oto kilka ex praktycznych przykładów.

Zamiana tekstu foo na bar w pliku:

ex -s +%s/foo/bar/ge -cwq file.txt

Usuwanie końcowych spacji dla wielu plików:

ex +'bufdo!%s/\s\+$//e' -cxa *.txt

Zobacz także:

 3
Author: kenorb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:30

Znajdź ./ - type f-name " plik*.txt "/ xargs sed-i-e 'S / abc / xyz / g'

 2
Author: J Ajay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-31 23:55:34

Możesz użyć polecenia rpl. Na przykład chcesz zmienić nazwę domeny w całym projekcie php.

rpl -ivRpd -x'.php' 'old.domain.name' 'new.domain.name' ./path_to_your_project_folder/  

To nie jest jasne bash przyczyny, ale jest to bardzo szybkie i przydatne. :)

 1
Author: zalex,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-12 08:34:49

Możesz również używać Pythona w skrypcie bash. Nie odniosłem większego sukcesu z niektórymi z najlepszych odpowiedzi tutaj, i okazało się, że to działa bez potrzeby pętli: {]}

#!/bin/bash
python
filetosearch = '/home/ubuntu/ip_table.txt'
texttoreplace = 'tcp443'
texttoinsert = 'udp1194'

s = open(filetosearch).read()
s = s.replace(texttoreplace, texttoinsert)
f = open(filetosearch, 'w')
f.write(s)
f.close()
quit()
 1
Author: micalith,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-03 18:44:19

Teraz, gdy ten wątek wydaje się zamieniać łańcuchy znaków (bajtów) na inny język niż bash, oto głupia implementacja C:

<!-- language: c -->

/**
 * Usage:
 *      ./replace "foobar" "foobaz" < input_file > output_file
 * Note: input_file and output_file should be different
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef struct string_t {
    const char * value;
    size_t length;
} string;

struct parser_t {
    string match_text, replace_text;

    char * match_buffer;
    unsigned int match_buffer_index;

    enum { STATE_INVALID, STATE_IN, STATE_OUT } state;
};

void parser_init(struct parser_t * parser, 
                 const char * match_text,
                 const char * replace_text)
{
    memset(parser, 0, sizeof(struct parser_t));

    parser->match_text.value = match_text;
    parser->match_text.length = strlen(match_text);
    parser->replace_text.value = replace_text;
    parser->replace_text.length = strlen(replace_text);
    parser->state = STATE_OUT;
    parser->match_buffer = malloc(parser->match_text.length);
}

void parser_free(struct parser_t * parser)
{
    free(parser->match_buffer);
}

void output_char(char current_char)
{
    fwrite(&current_char, sizeof(char), 1, stdout);
}

void buffer_match(struct parser_t * parser, char current_char)
{
    parser->match_buffer[parser->match_buffer_index++] = current_char;
}

void buffer_clear(struct parser_t * parser)
{
    parser->match_buffer_index = 0;
}

void buffer_flush(struct parser_t * parser)
{
    if (parser->match_buffer_index > 0) {
        fwrite(parser->match_buffer, sizeof(char), parser->match_buffer_index, stdout);
        buffer_clear(parser);
    }
}

int process_state_in(struct parser_t * parser, char current_char)
{
    if (parser->match_text.value[parser->match_buffer_index] == current_char) {
        buffer_match(parser, current_char);

        return STATE_IN;
    }

    if (parser->match_buffer_index == parser->match_text.length) {
        fwrite(parser->replace_text.value, sizeof(char), parser->replace_text.length, stdout);
        buffer_clear(parser);

        output_char(current_char);

        return STATE_OUT;
    }

    if (parser->match_text.value[parser->match_buffer_index] != current_char) {
        buffer_flush(parser);
        output_char(current_char);

        return STATE_OUT;
    }

    return STATE_INVALID;
}

int process_state_out(struct parser_t * parser, char current_char)
{
    if (parser->match_text.value[parser->match_buffer_index] == current_char) {
        buffer_match(parser, current_char);

        return STATE_IN;
    } 

    if (parser->match_text.value[parser->match_buffer_index] != current_char) {
        buffer_flush(parser);
        output_char(current_char);

        return STATE_OUT;
    }

    return STATE_INVALID;
}

int main(int argc, char *argv[])
{
    char current_char;
    struct parser_t parser;

    if (argc != 3) {
        fprintf(stdout, "Usage:\n\t%s match_text replace_text < in_file > out_file\n\t# note in_file and out_file should be different.\n", argv[0]);
        return 0;
    }

    parser_init(&parser, argv[1], argv[2]);

    while (fread(&current_char, sizeof(char), 1, stdin) != 0) {
        switch (parser.state) {
            case STATE_IN:
            {
                parser.state = process_state_in(&parser, current_char);
            }
            break;
            case STATE_OUT:
            {
                parser.state = process_state_out(&parser, current_char);
            }
            break;
            default:
                fprintf(stderr, "Error: Invalid state.\n");
                return -1;
            break;
        }
    }

    parser_free(&parser);

    return 0;
}

Skompiluj i uruchom:

$ cc replace.c -oreplace
$ ./replace "foobar" "foobaz" < input_file > output_file
 -1
Author: neuro_sys,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-11 21:45:23