find 및 sed를 사용할 때 파일 이름을 얻는 방법

find 및 sed를 사용할 때 파일 이름을 얻는 방법

저는 특정 파일에 sed를 적용한 다음 변경된 파일을 나열하여 어떤 파일이 수정되었는지 알 수 있는 스크립트를 작성 중입니다.

이것이 내가 sed를 찾아 사용한 방법입니다.

find . -type f -a \( -name "*.txt" -o -name "*.git"\) -a -exec sed -i -e "s/"str1"/"str2"/g" {} +

변경된 파일의 파일명을 어떻게 출력하나요? 읽기 쉽도록 정렬된 순서로 인쇄하고 싶습니다.

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

sed -i 's/$pattern/$new_pattern/w changelog.txt' $filename
if [ -s changelog.txt ]; then
  # CHANGES MADE, DO SOME STUFF HERE
else
  # NO CHANGES MADE, DO SOME OTHER STUFF HERE
fi

하지만 find와 sed를 동시에 사용할 때 어떻게 해야 할까요? 매뉴얼 페이지를 확인하고 많은 것을 시도했지만 아무것도 작동하지 않았습니다.

답변1

sed -is스크립트 의 명령 sed성공 여부 에 관계없이 파일을 다시 작성합니다(효과적으로 파일의 완전한 새 복사본 만들기) .

sed -i여기서는 .: 을 포함하지 않고 GNU 도구를 사용 하지 않으려고 합니다 str1.

find . -type f \( -name "*.txt" -o -name "*.git" \) -size +3c \
  -exec grep -lZ str1 {} + |
  while IFS= read -rd '' file; do
    sed -i 's/str1/str2/g' "$file" &&
      printf '%s\n' "$file"
  done

성공하면(새 버전의 파일을 생성하는 동안 오류가 발생하지 않음) sed파일 이름이 포함된 각 파일에 대해 하나씩 실행 str1하고 파일 이름을 인쇄합니다.sed

또는 파일당 grep하나씩 실행할 수 있습니다 sed.

find . -type f \( -name "*.txt" -o -name "*.git" \) \( -size +3c \
  -exec grep -q str1 {} \; \
  -exec sed -i 's/str1/str2/g' {} \; \
  -printf '"%p" was modified\n' \
    -o -printf '"%p" was not modified\n"' \)

답변2

귀하의 sed명령(정확하게 인용됨):

sed 's/str1/str2/g'

str1그러면 모든 항목이 로 변경됩니다 str2. 포함된 파일 목록은 str1다음에서 얻을 수 있습니다 grep -l 'str1'.

find . -type f \( -name '*.txt' -o -name '*.git' \) \
    -exec grep -l 'str1' {} \; \
    -exec sed -i 's/str1/str2/g' {} + >changelist.txt

여기에서는 grep -l리디렉션이 제공됩니다 . 이는 해당 패턴이 포함된 파일에서만 실행되는 필터 changelist.txt역할도 합니다 . 그러면 변경 사항이 파일에 적용됩니다(그리고 조용히 유지됩니다).sedsedsed -i

또는 find문자열이 포함된 파일의 경로 이름을 인쇄해 보겠습니다.

find . -type f \( -name '*.txt' -o -name '*.git' \) \
    -exec grep -q 'str1' {} \; \
    -print \
    -exec sed -i 's/str1/str2/g' {} + >changelist.txt

관련된:

답변3

제가 이 질문에 대답하는 방식은 다음과 같습니다. 선행은 이루기가 어렵다재미를 위해. 그래서 당신이 이것을 좋아한다면 당신은 그에게 투표해야 합니다. 이는 find를 한 번만 호출하면서 여러 명령을 실행하고 더 많은 복잡성을 처리하는 방법을 보여주기 때문에 약간 다릅니다.

답변

일치하는 항목이 발견되면 Grep은 True로 평가됩니다(예: $? == 0). 따라서 grep -l 'str1' filename파일 이름에 str1이 있는 경우에도 마찬가지입니다. 이 명령을 sed 명령에 연결하면 &&grep이 일치할 때만 sed가 실행되도록 할 수 있습니다.

다음 명령은 sed가 변경을 원하는 경우에만 파일 이름을 출력합니다.

grep -l 'str1' filename && sed -i 's/str1/str2/g' filename

-exec에서 직접 사용할 수 없으므로 &&bash 호출로 래핑합니다.

find ./ -type f \( -name '*.txt' -o -name '*.git' \) \
    -exec bash -c "grep -l 'str1' {} && sed -i 's/str1/str2/g' {}" \; > changelist.txt

이것과 Kusalananda의 답변 사이의 명백한 차이점은 grep이 str1과 일치하지 않으면 sed가 실행되지 않는다는 것입니다. Kusalananda의 답변에서 grep은 각 파일에 대해 실행되고 sed는 각 파일에 대해 실행됩니다. 파일 수에 따라 실행 시간에 큰 영향을 미칠 수 있습니다. OP의 질문에 따르면 아마도 전혀 큰 차이가 없을 것입니다.

할 수 있다grep -q로 바꾸고 grep -l, +으로 바꾸고, 삭제하여 \;그의 답변을 단순화합니다 -print.

find . -type f \( -name '*.txt' -o -name '*.git' \) \
    -exec grep -l 'str1' {} \; \
    -exec sed -i 's/str1/str2/g' {} \; >changelist.txt

이 모든 것은 단지 질책일 뿐입니다. 다음 단계에서는 bash -cin find -exec옵션을 사용하는 이유입니다. 누군가가 유용하다고 생각하기를 바랍니다.

내가 접근하는 이유

저는 sed를 find와 함께 사용하여 로그 파일의 일부를 인쇄하고 sed가 무엇이든 출력하는 경우에만 로그 파일 이름을 인쇄하고 싶기 때문에 여기에 왔습니다.

다음과 같은 내용이 포함된 로그가 있습니다.

    ---- lots of lines before ----
Failed:    0

--------------------------------------------------
Summary
--------------------------------------------------
( Cases/Passed/Failed)
Frequency Test           : (    69/    67/     2)
Carrier/Data Null Test   : (    14/    13/     1)
Total Harmonic Distortion: (     9/     9/     0)
Spur Test                : (     0/     0/     0)

--------------------------------------------------
failed Test
--------------------------------------------------
freq, rf2, 0.750e9, -70.0, pm 500,    pm 1.0
    ---- lots of lines after ----

sed가 테스트 요약을 감지한 경우에만 테스트 요약과 파일 이름을 인쇄하고 싶습니다.

따라서 여러 파일에 대해 다음과 같은 출력을 원합니다.

File: ./4662-0003-05132021-0953.log
Summary
--------------------------------------------------
( Cases/Passed/Failed)
Frequency Test           : (    69/     0/    69)
Carrier/Data Null Test   : (    14/     0/    14)
Total Harmonic Distortion: (     9/     9/     0)
Spur Test                : (     0/     0/     0)

File: ./4745-0001-05132021-1017.log
Summary
--------------------------------------------------
( Cases/Passed/Failed)
Frequency Test           : (    69/    68/     1)
Carrier/Data Null Test   : (    14/    14/     0)
Total Harmonic Distortion: (     9/     9/     0)
Spur Test                : (     0/     0/     0)

나는 다음 명령으로 이것을 달성했습니다.

find ./ -type f -name '*.log' \
    -exec bash -c "grep -q Summary {} && echo 'File: {}' && sed -n '/Summary/,/Spur/p' {} && echo" \;

분할 grep -q Summary ()하고 요약이 로그 파일에 표시되지 않으면 다음 항목이 실행되지 않습니다. sed -n '/Summary/,/Spur/p'"Summary"와 "Spur" 사이의 로그 부분만 인쇄됩니다.

-exec cmd {}와 -exec cmd +의 차이점

\;을 대신 사용하는 이유가 궁금할 수도 있습니다 +. 을 사용하면 +{}는 명령줄에 들어갈 수 있는 만큼의 파일 이름으로 대체됩니다. 이는 우리가 원하는 것이 아니며 find는 이 경우에도 이를 허용하지 않습니다.

사람들에게서 발견되었습니다:

   -exec command {} +
          This variant of the -exec action runs the specified command on the selected files, but the command line is built  by
          appending  each selected file name at the end; the total number of invocations of the command will be much less than
          the number of matched files.  The command line is built in much the same way that xargs builds  its  command  lines.
          Only  one  instance  of  `{}' is allowed within the command.  The command is executed in the starting directory.  If
          find encounters an error, this can sometimes cause an immediate exit, so some pending commands may  not  be  run  at
          all.  This variant of -exec always returns true.

결론적으로

허접한 내용이라 죄송하지만 누군가에게 도움이 되었으면 좋겠습니다.

답변4

exec원하는 작업을 수행하는 작은 스크립트를 작성하고 해당 스크립트를 find. 스크립트가 이미 있습니다. $filename로 바꾸면 $1이미 스크립트가 있는 것입니다. 귀하의 스크립트는 다음과 같은 형식을 취합니다.

#!/bin/bash
sed -i 's/$pattern/$new_pattern/' $1
echo $1 >> changelog

이 스크립트를 이라고 부르자 ed_notify. 이제 다음을 통해 선택한 파일에서 실행할 수 있습니다.

cat changelog >> changelog.old
rm changelog
find . -type f -a \( -name "*.txt" -o -name "*.git"\) -a -exec ed_notify {} \;

관련 정보