파일 이름에 와일드카드를 사용하는 sed 문제

파일 이름에 와일드카드를 사용하는 sed 문제

이것은 작동합니다:

sudo sed 's/good times/bad times/' Chapter1.html > output/Chapter1.html

이것은 작동하지 않습니다:

sudo sed 's/good times/bad times/' Chapter*.html > output/Chapter*.html

이것도 작동하지 않습니다.

sudo sed 's/good times/bad times/' *.html > output/*.html

50개의 챕터가 있으므로 sed에서 와일드카드를 사용하도록 할 수 있나요?

답변1

다른 사람들이 언급했듯이 여기에는 와일드카드뿐만 아니라 루프가 필요합니다.

예를 들어, for쉘 루프를 사용하여 이를 수행하려면 다음을 수행하십시오.

for f in ./*.html; do
  sed 's/good times/bad times/' "$f" > "output/$f"
done

그러면 변수가 f각 .html 파일 이름으로 설정되어 루프가 반복될 때마다 루프 내에서 코드가 실행됩니다. 단지 ../*.html*.html

f명령문 자체에는 for(값을 설정하는 곳이기 때문에) 간단한 단어가 있지만 $f변수가 루프 내부에서 사용되는 경우(확장되는 곳이기 때문에)에 주목하세요.

;변수 확장은 공백 문자나 쉘에 특별한 의미가 있는 기타 문자(예 : , &, >및 기타 여러 문자) 가 포함된 경우 스크립트가 손상되지 않도록(또는 더 나쁜 경우) 큰따옴표로 묶습니다 . 변수를 사용할 때 변수를 인용하지 않는 것이 아마도 쉘 스크립트 오류의 가장 큰 원인일 것입니다. 바라보다공백이나 기타 특수 문자 때문에 쉘 스크립트가 멈추는 이유는 무엇입니까?그리고$VAR 대 ${VAR} 및 인용 여부이유를 알아보세요.

원하는 변수 이름을 사용할 수 있습니다.

for Chapter in ./*.html; do
  sed 's/good times/bad times/' "$Chapter" > "output/$Chapter"
done

또한 주목할 가치가 있는 것은: 디렉토리에 .html 파일이 없으면 먼저 이 옵션을 활성화하지 않는 한 쉘은 f(또는 Chapter)을 리터럴 문자열로 설정합니다 . 에서 :*.htmlnullglobshopt -s nullglobman bash

nullglob

설정된 경우, bash는 어떤 파일과도 일치하지 않는 패턴(
위의 경로 이름 확장 참조)이 자신이 아닌 빈 문자열로 확장되도록 허용합니다.


그런데 저는 파일 이름이 명령줄 옵션 중 하나로 해석되는 것을 방지하기 위해 루프 ./*.html와 함께 이것을 사용하고 있습니다.for*.htmlsed

주석에서 @StéphaneChazelas가 언급했듯이 -e디렉토리에 시작하고 끝나는 파일 이름이 있으면 sed는 이를 실행할 sed 스크립트로 해석합니다. #.html이런 일이 일어날 가능성은 거의 없지만(배설물과 악의도 마찬가지입니다), 가능한 한 방어적으로 프로그래밍하는 것이 가장 좋습니다.

./*.html예를 들어 sed 대신 인수를 사용하면 파일 이름이 지정 -e1,$d되므로 -e1,$d#.html(완벽하게 유효한 파일 이름) 인수가 ./-e1,$dsed의 명령줄 옵션 중 하나로 해석되지 않는 것을 볼 수 있습니다. sed 옵션이 에 없습니다 ./.

또한: $f로 시작하기 때문에 ./파일 이름과 같은 출력은 foo.html으로 리디렉션됩니다 . 경로에 추가 요소가 있어도 여전히 동일한 대상으로 확인되므로 output/./foo.html이는 완전히 문제가 되지 않습니다 . ./우스꽝스러운 일조차 output/./././[a million more ./s]/foo.html그저output/foo.html

GNU sed(Linux의 표준 sed) 또는 (거의?) 최신 버전의 sed를 사용하는 경우 다음을 --사용하여 옵션 인수의 끝을 나타낼 수 있습니다.

for f in *.html; do
  sed 's/good times/bad times/' -- "$f" > "output/$f"
done

아니면 둘 다 수행하세요.

for f in ./*.html; do
  sed 's/good times/bad times/' -- "$f" > "output/$f"
done

답변2

쉘은 명령줄에서 와일드카드 문자를 확장하지만 실행 중인 명령은 이를 볼 수도 없고 어떻게 해야 할지 알지도 못합니다.

따라서 파일이 있고 a.html b.html실행 output/b.html output/c.html 하면

sed ... *.html > output/*.html

실제로 실행할 명령은 다음과 같습니다.

sed ... a.html b.html > output/b.html output/c.html

이는 구문 오류(두 파일로 리디렉션할 수 없음)이며 아마도 원하는 것이 아닐 것입니다.

여기서 해결책은 for 루프를 사용하고 *를 루프의 인덱스 변수로 바꾸는 것입니다. 파일 이름에 공백이 있는 경우 일부 인용이 필요합니다. 이 질문의 사본에는 이를 올바르게 수행하는 방법에 대한 많은 예가 있습니다.

답변3

파일이 포함된 작업 디렉토리에 있다고 가정하면 다음을 사용할 수 있습니다.find

$ find . -name 'Chapter*' -exec sed 's/good times/bad times/woutput/{}' {} 2> /dev/null \;

관련 정보