Найти и заменить внутри текстового файла из команды Bash


каков самый простой способ сделать поиск и замену для данной входной строки, скажем abc, и заменить на другую строку, скажем XYZ в файле /tmp/file.txt?

Я пишу приложение и использую IronPython для выполнения команд через SSH - но я не знаю Unix, что хорошо и не знаю, что искать.

Я слышал, что Bash, помимо интерфейса командной строки, может быть очень мощным языком сценариев. Итак, если это правда, я предполагаю, что вы можете выполнить вот такие действия.

могу ли я сделать это с помощью bash, и какой самый простой (одна строка) скрипт для достижения моей цели?

15 387

15 ответов:

самый простой способ-использовать sed (или perl):

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

который вызовет sed для выполнения редактирования на месте из-за . Это можно назвать от bash.

Если вы действительно хотите использовать только bash, то может работать следующее:

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

это циклы по каждой строке, делая подстановку, и запись во временный файл (не хочу, чтобы забить вход). Движение в конце просто перемещается временно к исходное имя.

манипуляции с файлами обычно выполняются не Bash, а программами, вызываемыми Bash, например:

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

The -i флаг говорит ему сделать замену на месте.

посмотреть man perlrun для получения более подробной информации, в том числе как сделать резервную копию исходного файла.

Я был удивлен, потому что я наткнулся на это...

есть "вместо" команда, которая поставляется с пакетом "mysql-server", так что если вы установили его попробовать:

# 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"

посмотреть человек вместо подробнее об этом...

Bash, как и другие оболочки, является всего лишь инструментом для координации других команд. Обычно вы пытаетесь использовать стандартные команды UNIX, но вы можете, конечно, использовать Bash для вызова чего угодно, включая ваши собственные скомпилированные программы, другие сценарии оболочки, скрипты Python и Perl и т. д.

в этом случае есть несколько способов сделать это.

Если вы хотите прочитать файл и записать его в другой файл, выполняя поиск / замену, используйте sed:

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

Если вы хотите отредактировать файл на месте (как если бы вы открыли файл в Редакторе, отредактировали его, а затем сохранили) предоставьте инструкции редактору строк "ex"

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

Ex-это как vi без полноэкранного режима. Вы можете дать ему те же команды, что и в приглашении vi':'.

нашел эту тему среди других, и я согласен, что она содержит наиболее полные ответы, поэтому я тоже добавляю:

1) sed и ed таковы useful...by рука!!! Посмотрите на этот код от @Johnny:

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

2) когда мое ограничение заключается в использовании его скриптом оболочки, то никакая переменная не может быть использована внутри вместо abc или XYZ! этой кажется, согласны с тем, что я понимаю по крайней мере. Итак, я не могу использовать:

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

но, что мы можем сделать? Как, @Johnny сказал использовать в то время как читать...- но, к сожалению, это еще не конец истории. Со мной хорошо работало следующее:

#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) Как можно видеть, если nullglob установлен тогда, он ведет себя странно, когда есть строка, содержащая * как в

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

что будет

<VirtualHost ServerName www.example.com

там нет концевой угловой кронштейн и Apache2 не может даже загрузить!

4) Этот вид разбора должен быть медленнее, чем один хит поиска и замены, но, как вы уже видели, там есть 4 переменные для 4 различных шаблонов поиска, работающих из одного только цикла разбора!

наиболее подходящее решение, которое я могу придумать с учетом данных предположений о проблеме.

Это старый пост, но для тех, кто хочет использовать переменные, как @centurian сказал, что одинарные кавычки ничего не будут расширены.

простой способ получить переменные - это сделать конкатенацию строк, так как это делается путем сопоставления в bash должно работать следующее:

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

вы можете использовать sed

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

используйте i для игнорирования, если вы не уверены, что текст для поиска-это abc или ABC или AbC,...

вы можете использовать find и sed, если вы не Теперь ваше имя файла:

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

найти и заменить во всех файлах python:

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

вы также можете использовать команду ed для поиска и замены в файле:

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

подробнее о bash-хакеры сайта

будьте осторожны, если вы заменяете URL-адреса символом"/".

пример того, как это сделать:

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

извлечено из: http://www.sysadmit.com/2015/07/linux-reemplazar-texto-en-archivos-con-sed.html

Если файл, над которым вы работаете, не так велик, и временное хранение его в переменной не является проблемой, то вы можете использовать замену строки Bash на весь файл сразу - нет необходимости перебирать его строка за строкой:

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

все содержимое файла будет рассматриваться как одна длинная строка, включая переносы строк.

XYZ может быть переменной, например,$replacement, и одним из преимуществ не использования sed здесь является то, что вам не нужно беспокоиться о том, что строка поиска или замены может содержать символ разделителя шаблона sed (обычно, но не обязательно, /). Недостатком является невозможность использования регулярных выражений или любой из более сложных операций sed.

чтобы редактировать текст в файле не интерактивно, вам нужен текстовый редактор на месте, такой как vim.

вот простой пример, как использовать его из командной строки:

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

это эквивалентно @ slim answer на ex редактор, который является в основном то же самое.

вот несколько ex практические примеры.

Замена текста foo С bar в файле:

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

удаление трейлинг пробелы для нескольких файлов:

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

Читайте также:

найти ./ - введите f-name " file*.тхт" | команды xargs СЕПГ -я -е с/АБВ/АБВ/г'

вы можете использовать команду РПЛ. Например, вы хотите изменить доменное имя во всем проекте php.

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

Это не понятно Баш причины, но это очень быстро и полезно. :)

вы также можете использовать python в скрипте bash. Я не имел большого успеха с некоторыми из лучших ответов здесь, и обнаружил, что это работает без необходимости циклов:

#!/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()

теперь, когда этот поток, похоже, превратился в замену строк (байтов) на любой другой язык, кроме bash, вот глупая реализация 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;
}

компиляция и запуск:

$ cc replace.c -oreplace
$ ./replace "foobar" "foobaz" < input_file > output_file