Изменение нескольких файлов

Следующая команда правильно изменяет содержимое двух файлов.

sed -i 's/abc/xyz/g' xaa1 xab1 

Но мне нужно сделать это, чтобы изменить несколько таких файлов динамически, и я не знаю имена файлов. Я хочу написать команду, которая будет читать все файлы из текущего каталога, начиная с xa* и sed, чтобы изменить содержимое файла.

Ответ 1

Еще лучше:

for i in xa*; do
    sed -i 's/asd/dfg/g' $i
done

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

Вот что происходит, когда слишком много файлов:

# grep -c aaa *
-bash: /bin/grep: Argument list too long
# for i in *; do grep -c aaa $i; done
0
... (output skipped)
#

Ответ 2

Я удивлен, что никто не упомянул аргумент -exec для поиска, который предназначен для этого типа сценария использования, хотя он запускает процесс для каждого соответствующего имени файла:

find . -type f -name 'xa*' -exec sed -i 's/asd/dsg/g' {} \;

В качестве альтернативы можно использовать xargs, который вызовет меньше процессов:

find . -type f -name 'xa*' | xargs sed -i 's/asd/dsg/g'

Или проще использовать вариант + exec вместо ; в find, чтобы позволить find предоставить более одного файла на вызов подпроцесса:

find . -type f -name 'xa*' -exec sed -i 's/asd/dsg/g' {} +

Ответ 3

Вы можете использовать grep и sed вместе. Это позволяет рекурсивно искать подкаталоги.

Linux: grep -r -l <old> * | xargs sed -i 's/<old>/<new>/g'
OS X: grep -r -l <old> * | xargs sed -i '' 's/<old>/<new>/g'

For grep:
    -r recursively searches subdirectories 
    -l prints file names that contain matches
For sed:
    -i extension (Note: An argument needs to be provided on OS X)

Ответ 4

Эти команды не будут работать в стандартном sed, который поставляется с Mac OS X.

От man 1 sed:

-i extension
             Edit files in-place, saving backups with the specified
             extension.  If a zero-length extension is given, no backup 
             will be saved.  It is not recommended to give a zero-length
             extension when in-place editing files, as you risk corruption
             or partial content in situations where disk space is exhausted, etc.

Пытались

sed -i '.bak' 's/old/new/g' logfile*

и

for i in logfile*; do sed -i '.bak' 's/old/new/g' $i; done

Оба работают нормально.

Ответ 5

@PaulR разместил это как комментарий, но люди должны рассматривать его как ответ (и этот ответ лучше всего подходит для моих нужд):

sed -i 's/abc/xyz/g' xa*

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

Ответ 6

Еще один универсальный способ - использовать find:

sed -i 's/asd/dsg/g' $(find . -type f -name 'xa*')

Ответ 7

Я использую find для аналогичной задачи. Это довольно просто: вы должны передать его как аргумент для sed следующим образом:

sed -i 's/EXPRESSION/REPLACEMENT/g' `find -name "FILE.REGEX"`

Таким образом вам не нужно писать сложные циклы, и вы можете просто увидеть, какие файлы вы собираетесь изменить, просто запустите find перед запуском sed.

Ответ 8

u может сделать

' xxxx' текст u и заменит его на yyyy

grep -Rn '**xxxx**' /path | awk -F: '{print $1}' | xargs sed -i 's/**xxxx**/**yyyy**/'

Ответ 9

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

Используя словарь /hashMap (ассоциативный массив) и переменные для команды sed, мы можем перебрать массив, чтобы заменить несколько строк. Включение подстановочного знака в name_pattern позволит заменить на месте в файлах шаблон (это может быть что-то вроде name_pattern='File*.txt') в определенном каталоге (source_dir). Все изменения записываются в logfile в destin_dir

#!/bin/bash
source_dir=source_path
destin_dir=destin_path
logfile='sedOutput.txt'
name_pattern='File.txt'

echo "--Begin $(date)--" | tee -a $destin_dir/$logfile
echo "Source_DIR=$source_dir destin_DIR=$destin_dir "

declare -A pairs=( 
    ['WHAT1']='FOR1'
    ['OTHER_string_to replace']='string replaced'
)

for i in "${!pairs[@]}"; do
    j=${pairs[$i]}
    echo "[$i]=$j"
    replace_what=$i
    replace_for=$j
    echo " "
    echo "Replace: $replace_what for: $replace_for"
    find $source_dir -name $name_pattern | xargs sed -i "s/$replace_what/$replace_for/g" 
    find $source_dir -name $name_pattern | xargs -I{} grep -n "$replace_for" {} /dev/null | tee -a $destin_dir/$logfile
done

echo " "
echo "----End $(date)---" | tee -a $destin_dir/$logfile

Сначала объявляется массив пар, каждая пара является строкой замены, затем WHAT1 будет заменен на FOR1 а OTHER_string_to replace будет заменена на string replaced в файле File.txt. В цикле считывается массив, первый член пары извлекается как replace_what=$i а второй как replace_for=$j. Команда find ищет в каталоге имя файла (которое может содержать подстановочный знак), а команда sed -i заменяет те же файлы, которые были определены ранее. Наконец, я добавил grep перенаправленный в файл журнала, чтобы регистрировать изменения, внесенные в файл (ы).

Это сработало для меня в GNU Bash 4.3 sed 4.2.2 и основано на ответе ВасяНовикова за цикл по кортежам в bash.