it-swarm-korea.com

파일에서 문자열을 바꾸려면 어떻게해야합니까?

특정 검색 기준에 따라 파일에서 문자열을 바꾸는 것은 매우 일반적인 작업입니다. 내가 어떻게 할 수있는

  • 현재 디렉토리의 모든 파일에서 문자열 foobar로 바꾸시겠습니까?
  • 하위 디렉토리에 대해서도 동일하게 반복합니까?
  • 파일 이름이 다른 문자열과 일치하는 경우에만 바꾸시겠습니까?
  • 문자열이 특정 상황에서 발견 된 경우에만 교체 하시겠습니까?
  • 문자열이 특정 줄 번호에 있으면 바꾸시겠습니까?
  • 여러 개의 문자열을 같은 것으로 교체하십시오
  • 여러 문자열을 다른 대체물로 대체
791
terdon

좋은 r e pl acement Linux 도구는 rpl이며 원래 데비안 프로젝트 용으로 작성되었으므로 는 데비안 파생 배포판에서 apt-get install rpl과 함께 사용할 수 있으며 다른 배포판에서도 사용할 수 있지만 SourgeForge 에서 tar.gz 파일을 다운로드 할 수 있습니다.

가장 간단한 사용 예 :

 $ rpl old_string new_string test.txt

문자열에 공백이 있으면 따옴표로 묶어야합니다. 기본적으로 rpl대문자를 처리하지만 완전한 단어는 처리하지 않지만 -i 옵션을 사용하여 이러한 기본값을 변경할 수 있습니다 (무시) case) 및 -w (전체 단어). 여러 파일을 지정할 수도 있습니다.

 $ rpl -i -w "old string" "new string" test.txt test2.txt

또는 extensions (-x)을 지정하여 디렉토리에서 recursively (-R)을 검색하거나 검색하십시오.

 $ rpl -x .html -x .txt -R old_string new_string test*

-p (프롬 트) 옵션을 사용하여 대화식 모드에서 검색/대체 할 수도 있습니다.

출력에는 대체 된 파일/문자열 수와 검색 유형 (대소 문자 구분/대소 문자 구분, 전체/부분 단어)이 표시되지만 -q (quiet mode-v (verbose mode) 옵션을 사용하여 각 파일 및 디렉토리와 일치하는 행 번호를 나열하는 =) 옵션 또는 더 자세한 정보입니다.

기억해야 할 다른 옵션은 -e을 허용하는 regular expressions (명예 e scapes)이므로 탭 (\t ), 줄 바꿈 (\n) 등 심지어 -f ~ force permissions (물론 사용자가 쓰기 권한을 가지고있는 경우에만) 및 -d을 사용하여 수정 시간을 보존 할 수 있습니다`.

마지막으로, 어느 것이 정확하게 만들어 질지 확실하지 않으면 -s (시뮬레이션 모드)를 사용하십시오.

79
Fran

여러 파일을 검색하고 바꾸는 방법 제안합니다 :

Find와 sed를 사용할 수도 있지만 Perl의이 작은 라인은 훌륭하게 작동합니다.

Perl -pi -w -e 's/search/replace/g;' *.php
  • -e는 다음 코드 줄을 실행하는 것을 의미합니다.
  • -i는 내부 편집을 의미합니다
  • -w 경고 쓰기
  • -p 입력 파일을 반복하여 스크립트가 적용된 후 각 줄을 인쇄합니다.

내 최고의 결과는 Perl과 grep을 사용하여 얻은 것입니다 (파일에 검색 표현식이 있는지 확인하기 위해)

Perl -pi -w -e 's/search/replace/g;' $( grep -rl 'search' )

나는 이것을 사용했다 :

grep -r "old_string" -l | tr '\n' ' ' | xargs sed -i 's/old_string/new_string/g'
  1. old_string가 포함 된 모든 파일을 나열하십시오.

  2. 줄 바꿈을 공백으로 바꾸십시오 (파일 목록을 sed에 제공 할 수 있도록).

  3. 해당 파일에서 sed를 실행하여 이전 문자열을 새 문자열로 바꿉니다.

pdate : 공백이 포함 된 파일 이름에서 위의 결과가 실패합니다. 대신 다음을 사용하십시오.

grep --null -lr "old_string" | xargs --null sed -i 's/old_string/new_string/g'

15
o_o_o--

Ex 모드에서 Vim을 사용할 수 있습니다 :

현재 디렉토리의 모든 파일에서 문자열 ALF를 BRA로 바꾸시겠습니까?

for CHA in *
do
  ex -sc '%s/ALF/BRA/g' -cx "$CHA"
done

하위 디렉토리에 대해서도 동일하게 반복합니까?

find -type f -exec ex -sc '%s/ALF/BRA/g' -cx {} ';'

파일 이름이 다른 문자열과 일치하는 경우에만 바꾸시겠습니까?

for CHA in *.txt
do
  ex -sc '%s/ALF/BRA/g' -cx "$CHA"
done

문자열이 특정 상황에서 발견 된 경우에만 교체 하시겠습니까?

ex -sc 'g/DEL/s/ALF/BRA/g' -cx file

문자열이 특정 줄 번호에 있으면 바꾸시겠습니까?

ex -sc '2s/ALF/BRA/g' -cx file

여러 개의 문자열을 같은 것으로 교체하십시오

ex -sc '%s/\vALF|ECH/BRA/g' -cx file

여러 문자열을 다른 대체물로 대체

ex -sc '%s/ALF/BRA/g|%s/FOX/GOL/g' -cx file
15
Steven Penny

ripgrep (명령 이름 rg)는 grep 도구이지만 검색 및 바꾸기도 지원합니다.

$ cat ip.txt
dark blue and light blue
light orange
blue sky
$ # by default, line number is displayed if output destination is stdout
$ # by default, only lines that matched the given pattern is displayed
$ # 'blue' is search pattern and -r 'red' is replacement string
$ rg 'blue' -r 'red' ip.txt
1:dark red and light red
3:red sky

$ # --passthru option is useful to print all lines, whether or not it matched
$ # -N will disable line number prefix
$ # this command is similar to: sed 's/blue/red/g' ip.txt
$ rg --passthru -N 'blue' -r 'red' ip.txt
dark red and light red
light orange
red sky


rg는 현재 위치 옵션을 지원하지 않으므로 직접해야합니다.

$ # -N isn't needed here as output destination is a file
$ rg --passthru 'blue' -r 'red' ip.txt > tmp.txt && mv tmp.txt ip.txt
$ cat ip.txt
dark red and light red
light orange
red sky


정규 표현식 구문 및 기능에 대해서는 Rust regex documentation 를 참조하십시오. -P 스위치는 PCRE2 플레이버를 활성화합니다. rg는 기본적으로 유니 코드를 지원합니다.

$ # non-greedy quantifier is supported
$ echo 'food land bark sand band cue combat' | rg 'foo.*?ba' -r 'X'
Xrk sand band cue combat

$ # unicode support
$ echo 'fox:αλεπού,eagle:αετός' | rg '\p{L}+' -r '($0)'
(fox):(αλεπού),(eagle):(αετός)

$ # set operator example, remove all punctuation characters except . ! and ?
$ para='"Hi", there! How *are* you? All fine here.'
$ echo "$para" | rg '[[:punct:]--[.!?]]+' -r ''
Hi there! How are you? All fine here.

$ # use -P if you need even more advanced features
$ echo 'car bat cod map' | rg -P '(bat|map)(*SKIP)(*F)|\w+' -r '[$0]'
[car] bat [cod] map


grep처럼 -F 옵션을 사용하면 고정 문자열을 일치시킬 수 있으며 sed도 구현해야하는 편리한 옵션입니다.

$ printf '2.3/[4]*6\nfoo\n5.3-[4]*9\n' | rg --passthru -F '[4]*' -r '2'
2.3/26
foo
5.3-29


다른 편리한 옵션은 -U 여러 줄 일치를 가능하게합니다

$ # (?s) flag will allow . to match newline characters as well
$ printf '42\nHi there\nHave a Nice Day' | rg --passthru -U '(?s)the.*ice' -r ''
42
Hi  Day


rg 도스 스타일 파일도 처리 할 수 ​​있습니다

$ # same as: sed -E 's/\w+(\r?)$/123\1/'
$ printf 'hi there\r\ngood day\r\n' | rg --passthru --crlf '\w+$' -r '123'
hi 123
good 123


rg의 또 다른 장점은 sed보다 빠르다는 것입니다.

$ # for small files, initial processing time of rg is a large component
$ time echo 'aba' | sed 's/a/b/g' > f1
real    0m0.002s
$ time echo 'aba' | rg --passthru 'a' -r 'b' > f2
real    0m0.007s

$ # for larger files, rg is likely to be faster
$ # 6.2M sample ASCII file
$ wget https://norvig.com/big.txt    
$ time LC_ALL=C sed 's/\bcat\b/dog/g' big.txt > f1
real    0m0.060s
$ time rg --passthru '\bcat\b' -r 'dog' big.txt > f2
real    0m0.048s
$ diff -s f1 f2
Files f1 and f2 are identical

$ time LC_ALL=C sed -E 's/\b(\w+)(\s+\1)+\b/\1/g' big.txt > f1
real    0m0.725s
$ time rg --no-pcre2-unicode --passthru -wP '(\w+)(\s+\1)+' -r '$1' big.txt > f2
real    0m0.093s
$ diff -s f1 f2
Files f1 and f2 are identical
3
Sundeep

나는 드라이 런 옵션을 제공하고 glob와 재귀 적으로 작동하는 무언가가 필요했으며 awksed로 시도한 후에 포기하고 대신 파이썬에서했습니다.

script 는 정규 표현식에 대한 glob 패턴 (예 : --glob="*.html")과 일치하는 모든 파일을 재귀 적으로 검색하고 대체 정규 표현식으로 바꿉니다.

find_replace.py [--dir=my_folder] \
    --search-regex=<search_regex> \
    --replace-regex=<replace_regex> \
    --glob=[glob_pattern] \
    --dry-run

--search-regex와 같은 모든 긴 옵션에는 해당하는 짧은 옵션 (예 : -s)이 있습니다. 모든 옵션을 보려면 -h로 실행하십시오.

예를 들어, 모든 날짜를 2017-12-31에서 31-12-2017로 뒤집습니다.

python replace.py --glob=myfile.txt \
    --search-regex="(\d{4})-(\d{2})-(\d{2})" \
    --replace-regex="\3-\2-\1" \
    --dry-run --verbose
import os
import fnmatch
import sys
import shutil
import re

import argparse

def find_replace(cfg):
    search_pattern = re.compile(cfg.search_regex)

    if cfg.dry_run:
        print('THIS IS A DRY RUN -- NO FILES WILL BE CHANGED!')

    for path, dirs, files in os.walk(os.path.abspath(cfg.dir)):
        for filename in fnmatch.filter(files, cfg.glob):

            if cfg.print_parent_folder:
                pardir = os.path.normpath(os.path.join(path, '..'))
                pardir = os.path.split(pardir)[-1]
                print('[%s]' % pardir)
            filepath = os.path.join(path, filename)

            # backup original file
            if cfg.create_backup:
                backup_path = filepath + '.bak'

                while os.path.exists(backup_path):
                    backup_path += '.bak'
                print('DBG: creating backup', backup_path)
                shutil.copyfile(filepath, backup_path)

            with open(filepath) as f:
                old_text = f.read()

            all_matches = search_pattern.findall(old_text)

            if all_matches:

                print('Found {} matches in file {}'.format(len(all_matches), filename))

                new_text = search_pattern.sub(cfg.replace_regex, old_text)

                if not cfg.dry_run:
                    with open(filepath, "w") as f:
                        print('DBG: replacing in file', filepath)
                        f.write(new_text)
                else:
                    for idx, matches in enumerate(all_matches):
                        print("Match #{}: {}".format(idx, matches))

                    print("NEW TEXT:\n{}".format(new_text))

            Elif cfg.verbose:
                print('File {} does not contain search regex "{}"'.format(filename, cfg.search_regex))


if __name__ == '__main__':

    parser = argparse.ArgumentParser(description='''DESCRIPTION:
    Find and replace recursively from the given folder using regular expressions''',
                                     formatter_class=argparse.RawDescriptionHelpFormatter,
                                     epilog='''USAGE:
    {0} -d [my_folder] -s <search_regex> -r <replace_regex> -g [glob_pattern]

    '''.format(os.path.basename(sys.argv[0])))

    parser.add_argument('--dir', '-d',
                        help='folder to search in; by default current folder',
                        default='.')

    parser.add_argument('--search-regex', '-s',
                        help='search regex',
                        required=True)

    parser.add_argument('--replace-regex', '-r',
                        help='replacement regex',
                        required=True)

    parser.add_argument('--glob', '-g',
                        help='glob pattern, i.e. *.html',
                        default="*.*")

    parser.add_argument('--dry-run', '-dr',
                        action='store_true',
                        help="don't replace anything just show what is going to be done",
                        default=False)

    parser.add_argument('--create-backup', '-b',
                        action='store_true',
                        help='Create backup files',
                        default=False)

    parser.add_argument('--verbose', '-v',
                        action='store_true',
                        help="Show files which don't match the search regex",
                        default=False)

    parser.add_argument('--print-parent-folder', '-p',
                        action='store_true',
                        help="Show the parent info for debug",
                        default=False)

    config = parser.parse_args(sys.argv[1:])

    find_replace(config)

Here 는 검색어와 강조 표시를 다른 색상으로 강조 표시하는 업데이트 된 스크립트 버전입니다.

3
ccpizza