Изменение нескольких файлов
Следующая команда правильно изменяет содержимое двух файлов.
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.