ファイル名にワイルドカードを使用する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

これは構文エラー(2つのファイルにリダイレクトできません)であり、おそらく望むものではありません。

ここで解決策は、forループを使用し、*をループのインデックス変数に置き換えることです。ファイル名にスペースが含まれている場合は、いくつかの引用が必要です。この質問のコピーには、これを正しく実行する方法の多くの例があります。

答え3

ファイルが含まれている作業ディレクトリーにあると仮定すると、以下を使用できます。find

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

関連情報