큰 폴더 계층 구조에서 텍스트를 바꾸는 방법은 무엇입니까?


11

일부 인스턴스를 제외하고 큰 파일 세트에서 일부 텍스트를 검색하고 바꾸고 싶습니다. 각 줄에 대해 해당 줄을 교체해야하는지 묻는 메시지가 표시됩니다. 정력의 유사한 :%s/from/to/gc합니다 (와 c확인을 위해 프롬프트), 그러나 일련의 폴더에서. 사용할 수있는 좋은 명령 줄 도구 나 스크립트가 있습니까?


올바른 형식 지정의 중요성에 대해 : 처음에는 작성하려고했던 것에 중점을 두지 s/from/to/g않고 형식 지정 결함 으로 명령을 읽었습니다 (Markdown으로 할 수는 없습니다. 및 HTML 태그). s/from/to/gcc<code><strong>
Gilles 'SO- 악마 그만해'

답변:


19

왜 vim을 사용하지 않습니까?

vim에서 모든 파일 열기

vim $(find . -type f)

또는 관련 파일 만 엽니 다 (Caleb에서 제안한대로)

vim $(grep 'from' . -Rl)

그런 다음 모든 버퍼에서 바꾸기를 실행하십시오.

:bufdo %s/from/to/gc | update

당신은 또한 그것을 할 수 sed있지만 내 sed 지식은 제한되어 있습니다.


고마워, 당신의 대답은 늦게 두 배를 타게했습니다 : 나는 대화 형 비트를 완전히 놓친다는 것을 깨달았습니다. 나는 그것이 sed (충분한 입력 / 출력 채널)로 가능하다고 생각하지 않습니다.
Gilles 'SO- 악마 그만'

1
일치하는 것으로 알려진 파일 만 열도록 grep대신 대신 사용하여 현재 버퍼에서 모든 파일을 열지 않으면 서 속도를 높일 수 있습니다 find. vim $(grep 'from' . -Rl)
Caleb

고마워요 .c (astreriks c 주위)가 필요합니까? 또는 형식 문제가 있습니까?
balki

@balki "포맷"문제입니다. 수정
Gert

5

-l -pe인수 ( -i) 로 전달 된 파일에 대해 줄 단위로 교체 ( ) 를 수행하도록 지시하는 작은 Perl 스크립트로 조잡한 작업을 수행 할 수 있습니다 .

perl -i -l -pe '
    if (/from/) {                            # is the source text present on this line?
        printf STDERR ("%s: %s [y/N]? ", $ARGV, $_);  # display a prompt
        $r=<STDIN>;                                   # read user response
        if ($r =~ /^[Yy]/) {                          # if user entered Y:
            s/from/to/g;                              # replace all occurences on this line
    }' /path/to/files

가능한 개선 사항은 프롬프트의 일부를 색칠하고 "현재 파일에서 발생하는 모든 항목 바꾸기"와 같은 항목을 지원하는 것입니다. 줄마다 각 항목에 대해 별도로 프롬프트를 표시하는 것이 더 어렵습니다.

두 번째 부분은 파일과 일치합니다. 너무 많은 파일이 포함되어 있지 않고 zsh를 실행중인 경우 현재 디렉토리 및 해당 서브 디렉토리의 모든 파일을 재귀 적으로 일치시킬 수 있습니다.

perl -i -l -pe '…' **/*(.)

쉘이 bash ≥4이면을 실행할 수 perl … **/*있지만 sed는 디렉토리에서 실행하려고 시도하고 실패하기 때문에 잘못된 오류 메시지를 생성합니다. C 파일과 같은 파일 세트에서만 교체를 수행하려는 경우 일치를 제한 할 수 있습니다 (bash ≥4 또는 zsh에서 작동).

perl -i -l -pe '…' **/*.[hc]

교체 할 파일을보다 세밀하게 제어해야하거나 쉘에 재귀 디렉토리 일치 구문 **이 없거나 파일이 너무 많아서 "명령 줄이 너무 깁니다"오류가 발생하면을 사용하십시오 find. 예를 들어, 이름이 *.h있거나 *.c현재 디렉토리 및 해당 서브 디렉토리에있는 모든 파일 (이전 시스템의 경우)을 바꾸 려면 줄의 끝 \;대신에 사용해야 합니다 +( +양식이 더 빠르지 만 모든 곳에서 사용할 수는 없음).

find . -type f -name '*.[hc]' -exec perl -i -l -pe '…' {} +

즉, 상호 작용이 필요한 경우 대화 형 편집기를 사용합니다. Gert는 Vim 에서이 방법을 보여 주었지만 검색하려는 모든 파일을 열어야하지만 많은 경우 문제가 될 수 있습니다.

Emacs에서는 다음과 같은 방법으로이를 수행 할 수 있습니다.

  1. M-x find-name-dired(최상위 디렉토리 M-x find-dired지정 ) 또는 (임의의 find명령 행 지정 )으로 파일 이름을 수집하십시오 .
  2. 결과 dired 버퍼 에서을 눌러 t모든 파일을 표시 한 다음 Q( dired-do-query-replace-regexp) 을 눌러 표시된 파일에 대한 프롬프트로 교체를 수행하십시오.

1

sdiff여기 ( http://www.gnu.org/software/diffutils/manual/diffutils.html#Invoking-sdiff 참조 )가 유용 할 수 있습니다. 그것으로 당신은 대화식 패치를 할 수 있습니다. 따라서 대체 작업을 사용하여 만든 임시 파일로 파일을 작성 sed하는 것이 가능한 해결책 일 수 있습니다.

# use file descriptor 3 to still allow use of stdin
while IFS= read -r -d '' file <&3; do

  # write the result of the replacement into a temporary file
  sed -r 's/something/something_else/g' -- "$file" > replacer_tmp

  if cmp -s -- "$file" replacer_tmp; then
    continue; # nothing was replaced
  fi

  echo "There is something to replace in '$file'! Starting interactive diff."
  echo

  sdiff -o "$file" -s -d -- "$file" replacer_tmp

  echo

done 3< <(find . -type f -print0)

(POSIX 이외의 프로세스 대체를 사용하고 read -d예를 들어 지원되는 파일 루프 bash)

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.