Linux sed во всех файлах

Change multiple files

But what I need to do is to change several such files dynamically and I do not know the file names. I want to write a command that will read all the files from current directory starting with xa* and sed should change the file contents.

I did @PaulR solution and it worked, but what I don’t understand is all these complicated other answers! What is missing from your solution?

@SoheilRahsaz sometimes when there is too much files the shell will complain about the argument list length as demonstrated by the top answer

11 Answers 11

I’m surprised nobody has mentioned the -exec argument to find, which is intended for this type of use-case, although it will start a process for each matching file name:

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

Alternatively, one could use xargs, which will invoke fewer processes:

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

Or more simply use the + exec variant instead of ; in find to allow find to provide more than one file per subprocess call:

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

I had to modify the command in this answer like so: find ./ -type f -name ‘xa*’ -exec sed -i » ‘s/asd/dsg/g’ <> \; that’s the location for the find command ./ and a pair of single quotes after -i for OSX.

The find command works as it is supplied by ealfonso, ./ is equal to . and after -i has only the backupsuffix parameter.

The -exec option of find along with <> + is sufficient to solve the problem as stated, and should be fine for most requirements. But xargs is a better choice in general because it also allows parallel processing with the -p option. When your glob expansion is large enough to overflow your command line length, you are likely to also benefit from a speedup over a sequential run.

I don’t know why none of these options work for me as I have used them successfully on *nix machines. I’m in terminal on a Mac though now.

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

because nobody knows how many files are there, and it’s easy to break command line limits.

Here’s what happens when there are too many files:

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

If there are that many files, you’ll break the command line limit in the for command. To protect yourself from that, you’d have to use find . | xargs .

I don’t know the implementation, but the «xa*» pattern does have to get expanded at some point. Does the shell do the expansion differently for for than it does for echo or grep ?

see the updated answer. if you need more info, please, ask an official question, so people could help you.

In the sed command, you need to use «$i» instead of $i to avoid word splitting on filenames with spaces. Otherwise this is very nice.

Regarding the list, I believe the difference is that for is part of the language syntax, not even just a builtin. For sed -i ‘s/old/new’ * , the expansion of * must ALL be passed as an arglist to sed, and I’m fairly sure this has to happen before the sed process can even be started. Using the for loop, the full arglist (the expansion of * ) never gets passed as a command, only stored in the shell memory and iterated through. I don’t have any reference for this at all though, it just seems probable that is the difference. (I’d love to hear from someone more knowledgeable. )

Читайте также:  What is open files in linux

You could use grep and sed together. This allows you to search subdirectories recursively.

Linux: grep -r -l * | xargs sed -i 's///g' OS X: grep -r -l * | xargs sed -i '' 's///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) 

Bonus of this method for me was I could slip in a grep -v to avoid git folders grep -rl . | grep -v \.git | xargs sed -i ‘s///g’

Those commands won’t work in the default sed that comes with Mac OS X.

-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 

@sumek Here’s an example terminal session on OS X that shows sed replacing all occurrences: GitHub Gist

I used this to replace two different lines in all my website config files with a one-liner below. sed -i.bak «s/supercache_proxy_config/proxy_includes\/supercache_config/g; s/basic_proxy_config/proxy_include\/basic_proxy_config/g» sites-available/* Don’t forget to delete the *.bak files when you are done for file system hygiene sake.

@PaulR posted this as a comment, but people should view it as an answer (and this answer works best for my needs):

This will work for a moderate amount of files, probably on the order of tens, but probably not on the order of millions.

Assume you have forward-slashes in your replacements. Another example with filepaths sed -i ‘s|auth-user-pass nordvpn.txt|auth-user-pass /etc/openvpn/nordvpn.txt|g’ *.ovpn .

Another more versatile way is to use find :

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

the output of that find command gets expanded, so this doesn’t address the issue. Instead you should use -exec

@erjoalgo this works because the sed command can handle multiple input files. Expansion of the find command is exactly was is needed to make it work.

That limit is dependent only on the memory resources available to the machine and it’s exactly the same as the limit for exec.

That is simply not true. In your command above, the $(find . . ) gets expanded into a single command, which could be very long if there are many matching files. If it is too long (for example in my system the limit is around 2097152 characters) you might get an error: «Argument list too long» and the command will fail. Please google this error to get some background on this.

Читайте также:  Kerberos аутентификация linux astra

I’m using find for similar task. It is quite simple: you have to pass it as an argument for sed like this:

sed -i ‘s/EXPRESSION/REPLACEMENT/g’ `find -name «FILE.REGEX»`

This way you don’t have to write complex loops, and it is simple to see, which files you are going to change, just run find before you run sed .

xxxx‘ text u search and will replace it with ‘yyyy

grep -Rn '**xxxx**' /path | awk -F: '' | xargs sed -i 's/**xxxx**/**yyyy**/' 

There’s some good answers above. I thought I’d throw in one more that is succinct and parallelizable, using GNU parallel, which I often prefer to xargs :

parallel sed -i 's/abc/xyz/g' <> . xa* 

Combine this with the -j N option to run N jobs in parallel.

If you are able to run a script, here is what I did for a similar situation:

Using a dictionary/hashMap (associative array) and variables for the sed command, we can loop through the array to replace several strings. Including a wildcard in the name_pattern will allow to replace in-place in files with a pattern (this could be something like name_pattern=’File*.txt’ ) in a specific directory ( source_dir ). All the changes are written in the logfile in the 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 "$"; do j=$ 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 

First, the pairs array is declared, each pair is a replacement string, then WHAT1 will be replaced for FOR1 and OTHER_string_to replace will be replaced for string replaced in the file File.txt . In the loop the array is read, the first member of the pair is retrieved as replace_what=$i and the second as replace_for=$j . The find command searches in the directory the filename (that may contain a wildcard) and the sed -i command replaces in the same file(s) what was previously defined. Finally I added a grep redirected to the logfile to log the changes made in the file(s).

This worked for me in GNU Bash 4.3 sed 4.2.2 and based upon VasyaNovikov’s answer for Loop over tuples in bash.

Источник

Linux и Android

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

sed — это потоковый редактор. Он может выполнять основные операции с текстом над файлами и входными потоками, такими как конвейеры. С помощью sed вы можете искать, находить и заменять, вставлять и удалять слова и строки. Он поддерживает базовые и расширенные регулярные выражения, которые позволяют сопоставлять сложные шаблоны.

В этой статье мы поговорим о том, как находить и заменять строки с помощью sed. Мы также покажем вам, как выполнять рекурсивный поиск и замену.

Существует несколько версий sed, с некоторыми функциональными отличиями между ними. macOS использует версию BSD, а большинство дистрибутивов Linux поставляются с предварительно установленной GNU sed по умолчанию. Мы будем использовать версию GNU.

  • -i — По умолчанию sed записывает свой вывод в стандартный вывод. Эта опция указывает sed редактировать файлы на месте. Если указано расширение (например, -i.bak), будет создана резервная копия исходного файла.
  • s — Команда замены, вероятно, наиболее часто используемая команда в sed.
  • / / / — Разделитель символов. Это может быть любой символ, но обычно используется символ косой черты (/).
  • SEARCH_REGEX — Обычная строка или регулярное выражение для поиска.
  • REPLACEMENT — Строка замены.
  • g — Флаг глобальной замены. По умолчанию sed читает файл построчно и изменяет только первое вхождение SEARCH_REGEX в строке. Если указан флаг замены, будут заменены все вхождения.
  • INPUTFILE — Имя файла, для которого вы хотите выполнить команду.
Читайте также:  Mount ntfs in linux mint

Давайте рассмотрим примеры использования команды sed для поиска и замены текста в файлах с некоторыми из его наиболее часто используемых опций и флагов.

123 Foo foo foo foo /bin/bash Ubuntu foobar 456

Как вы могли заметить, в предыдущем примере подстрока foo внутри строки foobar также заменяется. Если такое поведение вас не устраивает, используйте выражение «boundery» (\b) на обоих концах строки поиска. Это гарантирует, что отдельные слова не совпадут.

Чтобы сделать сопоставление с шаблоном нечувствительным к регистру, используйте флаг I. В приведенном ниже примере мы используем флаги g и I:

Если вы хотите найти и заменить строку, содержащую символ разделителя (/), вам нужно использовать обратную косую черту (\), чтобы экранировать ее. Например, чтобы заменить /bin/bash на /usr/bin/zsh, вы бы использовали следующую команду:

Более простой и читаемый вариант — использовать другой символ-разделитель. Большинство людей используют вертикальную черту (|) или двоеточие (:), но вы можете использовать любой другой символ:

Вы также можете использовать регулярные выражения. Например, чтобы найти все трехзначные числа и заменить их строкой number:

Еще одна полезная особенность sed — вы можете использовать символ амперсанда &, который соответствует подходящему шаблону. Символ может быть использован несколько раз.

И последнее, но не менее важное: всегда полезно сделать резервную копию при редактировании файла с помощью sed. Для этого просто добавтьте расширение к опции -i. Например, чтобы отредактировать файл file.txt и сохранить исходный файл как file.txt.bak, вы должны ввести:

Иногда вам нужно рекурсивно искать в каталогах файлы, содержащие заданную строку, и заменять эту строку во всех файлах. Это можно сделать с помощью таких команд, как find или grep, для рекурсивного поиска файлов в каталоге и передачи имен файлов в sed.

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

Чтобы избежать проблем с файлами, содержащими пробел в их именах, используйте опцию -print0, которая указывает find печатать имя файла, с символом null после него, и направлять вывод в sed с помощью xargs -0:

Чтобы исключить каталог, используйте опцию -not -path. Например, если вы заменяете строку в локальном репозитории git, чтобы исключить все файлы, начинающиеся с точки (.), выполните:

Другой вариант — использовать команду grep для рекурсивного поиска всех файлов, содержащих заданный шаблон, а затем передать имена файлов в sed:

Хотя сначала это может показаться сложным, поиск и замена текста в файлах с помощью sed очень просты. Чтобы узнать больше о командах, опциях и флагах sed, почитайте руководство GNU sed и учебник Grymoire sed.

Источник

Оцените статью
Adblock
detector