명령줄에서 재귀적으로 찾기 및 바꾸기를 수행하려면 어떻게 해야 합니까?

명령줄에서 재귀적으로 찾기 및 바꾸기를 수행하려면 어떻게 해야 합니까?

bash 또는 zshell과 같은 쉘을 사용하여 재귀적인 '찾기 및 바꾸기'를 어떻게 수행할 수 있습니까? 즉, 이 디렉토리와 하위 디렉토리의 모든 파일에서 'foo'를 모두 'bar'로 바꾸고 싶습니다.

답변1

이 명령이 이를 수행합니다(Mac OS X Lion 및 Kubuntu Linux 모두에서 테스트됨).

# Recursively find and replace in files
find . -type f -name "*.txt" -print0 | xargs -0 sed -i '' -e 's/foo/bar/g'

작동 방식은 다음과 같습니다.

  1. find . -type f -name '*.txt'현재 디렉토리( .) 이하 에서 -type f이름이 다음으로 끝나는 모든 일반 파일( ) 을 찾습니다..txt
  2. |해당 명령의 출력(파일 이름 목록)을 다음 명령으로 전달합니다.
  3. xargs해당 파일 이름을 모아서 하나씩 건네줍니다.sed
  4. sed -i '' -e 's/foo/bar/g'"백업 없이 파일을 제자리에서 편집하고 다음 대체( s/foo/bar)를 한 줄에 여러 번 수행합니다( /g)"를 의미합니다( 참조 man sed).

4번째 줄의 '백업 없음' 부분은 괜찮습니다. 왜냐하면 제가 변경하는 파일은 어쨌든 버전 관리 하에 있으므로 실수가 있으면 쉽게 실행 취소할 수 있기 때문입니다.

이를 기억할 필요가 없도록 다음과 같이 대화형 bash 스크립트를 사용합니다.

#!/bin/bash
# find_and_replace.sh

echo "Find and replace in current directory!"
echo "File pattern to look for? (eg '*.txt')"
read filepattern
echo "Existing string?"
read existing
echo "Replacement string?"
read replacement
echo "Replacing all occurences of $existing with $replacement in files matching $filepattern"

find . -type f -name $filepattern -print0 | xargs -0 sed -i '' -e "s/$existing/$replacement/g"

답변2

find . -type f -name "*.txt" -exec sed -i'' -e 's/foo/bar/g' {} +

이렇게 하면 xargs종속성이 제거됩니다.

답변3

Git을 사용하는 경우 다음을 수행할 수 있습니다.

git grep -lz foo | xargs -0 sed -i '' -e 's/foo/bar/g'

-l파일 이름만 나열합니다. -z각 결과 뒤에 널 바이트를 인쇄합니다.

프로젝트의 일부 파일에는 파일 끝에 개행 문자가 없었고 sed는 다른 변경 사항이 없는 경우에도 개행 문자를 추가했기 때문에 이 작업을 수행하게 되었습니다. (파일 끝에 개행 문자가 있어야 하는지 여부에 대해서는 언급하지 않습니다.

답변4

여기에 제가 사용하는 zsh/perl 함수가 있습니다:

change () {
        from=$1 
        shift
        to=$1 
        shift
        for file in $*
        do
                perl -i.bak -p -e "s{$from}{$to}g;" $file
                echo "Changing $from to $to in $file"
        done
}

그리고 나는 그것을 다음을 사용하여 실행합니다.

$ change foo bar **/*.java

(예를 들어)

관련 정보