[bash] find 및 sed를 사용하여 재귀 적으로 파일 이름 바꾸기

여러 디렉토리를 살펴보고 _test.rb로 끝나는 모든 파일의 이름을 _spec.rb로 끝내고 싶습니다. bash로하는 방법을 전혀 알아 내지 못했던 것이므로 이번에는 그것을 못 박는 데 약간의 노력을 기울일 것이라고 생각했습니다. 나는 지금까지 짧게 말했지만 최선의 노력은 다음과 같습니다.

find spec -name "*_test.rb" -exec echo mv {} `echo {} | sed s/test/spec/` \;

NB : exec 후에 추가 에코가 있으므로 테스트하는 동안 명령이 실행되는 대신 인쇄됩니다.

실행할 때 일치하는 각 파일 이름에 대한 출력은 다음과 같습니다.

mv original original

즉, sed에 의한 대체가 손실되었습니다. 트릭은 무엇입니까?



답변

이것은 sed문자열 {}을 입력으로 받기 때문에 발생합니다 .

find . -exec echo `echo "{}" | sed 's/./foo/g'` \;

foofoo디렉토리의 각 파일에 대해 재귀 적으로 인쇄 합니다. 이 동작의 이유는 파이프 라인이 전체 명령을 확장 할 때 셸에 의해 한 번 실행되기 때문입니다.

셸을 통해 명령을 실행하지 않고 파이프 라인이나 역 따옴표에 대한 개념이 없기 때문에 모든 파일에 대해 sed파이프 라인을 find실행 하는 방식으로 파이프 라인 을 인용 할 방법 find이 없습니다. GNU findutils 매뉴얼은 파이프 라인을 별도의 쉘 스크립트에 넣어 유사한 작업을 수행하는 방법을 설명합니다.

#!/bin/sh
echo "$1" | sed 's/_test.rb$/_spec.rb/'

(이 sh -c모든 것을 하나의 명령으로 수행하기 위해 사용하는 비뚤어진 방법 과 많은 따옴표가있을 수 있지만 시도하지 않을 것입니다.)


답변

원래 문제에 가장 가까운 방법으로 해결하려면 xargs “args per command line”옵션을 사용하는 것이 좋습니다.

find . -name "*_test.rb" | sed -e "p;s/test/spec/" | xargs -n2 mv

현재 작업 디렉토리에서 파일을 재귀 적으로 찾고 원래 파일 이름 ( p)과 수정 된 이름 ( s/test/spec/)을 반향하여 모두 mv쌍 ( xargs -n2)으로 공급합니다 . 이 경우 경로 자체에는 문자열이 포함되지 않아야합니다 test.


답변

다음과 같은 다른 방법을 고려할 수 있습니다.

for file in $(find . -name "*_test.rb")
do
  echo mv $file `echo $file | sed s/_test.rb$/_spec.rb/`
done


답변

이게 더 짧아요

find . -name '*_test.rb' -exec bash -c 'echo mv $0 ${0/test.rb/spec.rb}' {} \;


답변

원하는 경우 sed없이 수행 할 수 있습니다.

for i in `find -name '*_test.rb'` ; do mv $i ${i%%_test.rb}_spec.rb ; done

${var%%suffix}스트립 suffix의 값으로부터 var.

또는 sed를 사용하여 수행하려면 :

for i in `find -name '*_test.rb'` ; do mv $i `echo $i | sed 's/test/spec/'` ; done


답변

당신은 당신이 사용하고 있다고 언급 bash, 쉘로하는 경우에 당신이 실제로 필요하지 않습니다 findsed후 당신의 이름을 변경 배치를 달성하기 위해 …

bash쉘로 사용한다고 가정합니다 .

$ echo $SHELL
/bin/bash
$ _

… 그리고 소위 globstar쉘 옵션을 활성화했다고 가정합니다 .

$ shopt -p globstar
shopt -s globstar
$ _

… 그리고 마지막으로 rename유틸리티 ( util-linux-ng패키지에 있음)를 설치했다고 가정합니다.

$ which rename
/usr/bin/rename
$ _

… 그러면 다음과 같이 bash 한 줄짜리 일괄 이름 바꾸기를 수행 할 수 있습니다 .

$ rename _test _spec **/*_test.rb

( globstar쉘 옵션은 bash가 *_test.rb디렉토리 계층 구조에 얼마나 많이 중첩되어 있더라도 일치하는 모든 파일을 찾도록 보장합니다 … help shopt옵션 설정 방법을 찾는 데 사용 )


답변

가장 쉬운 방법 :

find . -name "*_test.rb" | xargs rename s/_test/_spec/

가장 빠른 방법 (프로세서 4 개가 있다고 가정) :

find . -name "*_test.rb" | xargs -P 4 rename s/_test/_spec/

처리 할 파일이 많은 경우 xargs에 파이프 된 파일 이름 목록으로 인해 결과 명령 줄이 허용되는 최대 길이를 초과 할 수 있습니다.

다음을 사용하여 시스템의 제한을 확인할 수 있습니다. getconf ARG_MAX

대부분의 리눅스 시스템에서 사용 free -b하거나 cat /proc/meminfo작업해야하는 RAM의 양을 찾을 수 있습니다 . 그렇지 않으면 top시스템 활동 모니터 앱을 사용하십시오.

더 안전한 방법 (작업 할 RAM이 1000000 바이트라고 가정) :

find . -name "*_test.rb" | xargs -s 1000000 rename s/_test/_spec/